diff --git a/.github/workflows/analyze_llvm.yml b/.github/workflows/analyze_llvm.yml deleted file mode 100644 index 2a687143..00000000 --- a/.github/workflows/analyze_llvm.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: Run code analysis (LLVM) - -on: - workflow_dispatch: - inputs: - branch: - required: true - type: string - description: Branch to analyze - -jobs: - code_analysis: - runs-on: [llvm] - - defaults: - run: - working-directory: cpp - - env: - SONAR_SERVER_URL: "https://sonarcloud.io" - SONAR_PROJECT_KEY: "uweseimet_scsi2pi" - SONAR_ORGANIZATION: "uweseimet-org" - SCAN_HOST: ${{ secrets.SCAN_HOST }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - - steps: - - uses: actions/checkout@v4 - with: - ref: ${{ inputs.branch }} - fetch-depth: 0 - - - name: Compile with coverage data - run: | - make -j $(nproc) EXTRA_FLAGS="-fprofile-instr-generate -fcoverage-mapping" CXX=clang++ DEBUG=1 DATABASE=1 test && - sed -e '1s/^/[\n/' -e '$s/,$/\n]/' obj/*.o.json > compile_commands.json - - - name: Convert coverage data - run: | - llvm-profdata merge default.profraw > coverage.dat && - llvm-cov show --show-branches=count --instr-profile coverage.dat bin/s2p_test > coverage.txt - - - name: Run sonar-scanner - run: | - scp compile_commands.json coverage.txt $SCAN_HOST:analysis/scsi2pi/cpp && - rm compile_commands.json default.profraw coverage.dat coverage.txt && - ssh $SCAN_HOST bin/analyze ${{ inputs.branch }} diff --git a/README.md b/README.md index d4703d76..e030c6c8 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,23 @@ # What is SCSI2Pi? -SCSI2Pi (or simply just S2P) is an alternative to the PiSCSI software for the PiSCSI/RaSCSI board. It provides an improved SCSI emulation, extendend functionality and new tools for the board's initiator mode. SCSI2Pi is compatible with the PiSCSI web UI and the SCSI Control app.
+SCSI2Pi is an advanced alternative to the PiSCSI software for the PiSCSI/RaSCSI board. It provides an improved SCSI emulation, extendend functionality and new tools for the board's initiator mode. SCSI2Pi is compatible with the PiSCSI web UI and the SCSI Control app.
You can switch from PiSCSI to SCSI2Pi (or back, if needed) in seconds, simply by installing/de-installing a package with the SCSI2Pi binaries. No time-consuming compilation is required.
-SCSI2Pi emulates several SCSI or SASI devices like hard drives, CD-ROM drives, printers or network adapters at the same time. You can easily add a range of devices to computers like 68k Macs, Atari ST/TT/Falcon030, Unix workstations, samplers or other computers with SCSI port. Compared to PiSCSI, SCSI2Pi offers numerous extensions, performance improvements and bug fixes. These add to the extensive changes and new features I contributed to the PiSCSI project in the past.
-SCSI2Pi was chosen as the name for this project because there are not that many choices left for names that contain both "SCSI" and "Pi" ;-). +SCSI2Pi emulates several SCSI or SASI devices like hard drives, CD-ROM drives, printers or network adapters at the same time. You can easily add a range of devices to computers like 68k Macs, Atari ST/TT/Falcon030, Amiga, Unix workstations, samplers or other computers with SCSI port. Compared to PiSCSI, SCSI2Pi offers numerous extensions, performance improvements, smaller and highly optimized binaries, and bug fixes. These add to the extensive changes and new features I contributed to the PiSCSI project in the past. -# How is SCSI2Pi related to PiSCSI? +# Who am I? -In the PiSCSI project there was not much interest in replacing old, often buggy or unnecessary code, or to improve the throughput. In addition, code reviewers were missing, which resulted in a rather long PR backlog, slowing down the development process. Developing software without being able to publish the results is not much fun. Further, long promised features on the PiSCSI roadmap and in tickets have not been addressed. This is why I decided to work on the emulation backend in a separate project. The major part of the PiSCSI C++ codebase has been contributed by me anyway.
-There was also no interest in further developing the SCSI emulation and exploiting the initiator mode feature of the FULLSPEC board. This mode, together with new SCSI2Pi command line tools, offers solutions for use cases that have never been addressed before. These tools also help with advanced testing. +In the past I was the main contributor for the PiSCSI SCSI emulation. I revised the backend architecture, added a remote interface and re-engineered most of the legacy C++ code. The code was updated to C++-20, which is the latest C++ standard you can currently use on the Pi. All in all this resulted in more modular code and drastically improved SonarQube code metrics. Besides adding numerous new features and improving the compatibility with many platforms, I also fixed a range of bugs in the legacy codebase and added an extensive set of unit tests.
+I am also the author of the SCSI Control app for Android, which is the remote control for your PiSCSI/RaSCSI boards. SCSI Control supports both SCSI2Pi and PiSCSI. The full range of app features requires SCSI2Pi, though. -# Who am I? +# How is SCSI2Pi related to PiSCSI? -In the past I was the main contributor for the PiSCSI emulation backend. I revised the backend architecture, added a remote interface and re-engineered most of the legacy C++ code. The code was updated to C++-20, which is the latest C++ standard you can currently use on the Pi. All in all this resulted in more modular code and drastically improved the SonarQube code metrics. Besides adding numerous new features and improving the compatibility with many platforms, I also fixed a range of bugs in the legacy codebase and added an extensive set of unit tests.
-I am also the author of the SCSI Control app for Android, which is the remote control for your PiSCSI/RaSCSI boards. SCSI Control supports both SCSI2Pi and PiSCSI. I also develop the HDDRIVER driver package and tools for Atari 32 bit computers. +In the PiSCSI project there was not much interest in replacing old, often buggy or unnecessary code, or to improve the data transfer rates. In addition, code reviewers were missing, which resulted in a rather long PR backlog, slowing down the development process. Unfortunately long promised features on the PiSCSI roadmap and user requests in tickets have not been addressed. This is why I decided to work on the emulation in a separate project, while staying compatible with the PiSCSI web interface. The major part of the PiSCSI C++ codebase has been contributed by me anyway.
+There was also not much interest in further developing the SCSI emulation and exploiting the initiator mode feature of the FULLSPEC board. This mode, together with new SCSI2Pi command line tools, offers solutions for use cases that have never been addressed before. These tools also help with advanced testing, making the emulation more robust and reliable. # SCSI2Pi goals -SCSI2Pi is not meant to completely replace the PiSCSI software, but only the actual SCSI/SASI emulation and the tools. For the PiSCSI project great work is still being done on the web interface, and on supporting users in social media. SCSI2Pi also addresses compatibility issues.
-There is no support for the X68000 platform, in particular not for the host bridge (SCBR) device. In PiSCSI the related code has always been in a bad shape, and nobody has been willing to test it. The other PiSCSI features (and more) are supported by SCSI2Pi - many of these I implemented anyway ;-). +SCSI2Pi is not meant to completely replace the PiSCSI software, but only the device emulation and the tools. For the PiSCSI project great work is still being done on the web interface, and on supporting users in social media.
+There is no SCSI2Pi support for the X68000 platform, in particular not for the host bridge (SCBR) device. In PiSCSI the respective code has always been in a bad shape, and nobody has been interested in testing it. The other PiSCSI features (and many more) are supported by SCSI2Pi - most of these I implemented anyway ;-). # SCSI2Pi website diff --git a/cpp/Makefile b/cpp/Makefile index 33565d57..7cadd8c3 100644 --- a/cpp/Makefile +++ b/cpp/Makefile @@ -29,12 +29,12 @@ ifeq ("$(shell uname -s)", "Linux") endif ifeq ("$(shell uname -s)", "FreeBSD") - AR = ar + AR = $(CROSS_COMPILE)ar CXXFLAGS += -fexperimental-library -I/usr/local/include -Wno-unused-parameter endif ifeq ("$(shell uname -s)", "NetBSD") - AR = ar + AR = $(CROSS_COMPILE)ar CXXFLAGS += -I/usr/pkg/include -Wno-unused-parameter -Wno-macro-redefined endif @@ -373,6 +373,7 @@ install: $(BINARIES) $(INSTALL_BIN)%: $(BINDIR)/% mkdir -p $(INSTALL_BIN) cp $< $@ + ## help Lists information about how to use the Makefile diff --git a/cpp/base/device.cpp b/cpp/base/device.cpp index e55e8a28..4ef852b1 100644 --- a/cpp/base/device.cpp +++ b/cpp/base/device.cpp @@ -1,13 +1,13 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2024 Uwe Seimet // //--------------------------------------------------------------------------- -#include #include +#include #include "shared/s2p_version.h" #include "device.h" @@ -47,12 +47,10 @@ void Device::SetProduct(const string &p, bool force) throw invalid_argument("Product '" + p + "' must have between 1 and 16 characters"); } - // Changing vital product data is not SCSI compliant - if (!product.empty() && !force) { - return; + // Changing existing vital product data is not SCSI compliant + if (product.empty() || force) { + product = p; } - - product = p; } void Device::SetRevision(const string &r) diff --git a/cpp/base/device.h b/cpp/base/device.h index 0016bb57..673bdeb7 100644 --- a/cpp/base/device.h +++ b/cpp/base/device.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2024 Uwe Seimet // @@ -8,7 +8,6 @@ #pragma once -#include #include "generated/s2p_interface.pb.h" #include "shared/s2p_util.h" @@ -43,6 +42,11 @@ class Device // NOSONAR The number of fields and methods is justified, the compl } virtual void Reset(); + void SetReset(bool b) + { + reset = b; + } + bool IsProtectable() const { return protectable; @@ -157,10 +161,6 @@ class Device // NOSONAR The number of fields and methods is justified, the compl { return reset; } - void SetReset(bool b) - { - reset = b; - } bool IsAttn() const { return attn; diff --git a/cpp/base/device_factory.cpp b/cpp/base/device_factory.cpp index e142b837..3b167a35 100644 --- a/cpp/base/device_factory.cpp +++ b/cpp/base/device_factory.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2023 Uwe Seimet // diff --git a/cpp/base/device_factory.h b/cpp/base/device_factory.h index 717c12c5..0e181aa3 100644 --- a/cpp/base/device_factory.h +++ b/cpp/base/device_factory.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2024 Uwe Seimet // @@ -13,7 +13,6 @@ #include #include "primary_device.h" #include "generated/s2p_interface.pb.h" -#include "shared/s2p_util.h" using namespace std; using namespace s2p_interface; diff --git a/cpp/base/device_logger.cpp b/cpp/base/device_logger.cpp index 513af798..115731f9 100644 --- a/cpp/base/device_logger.cpp +++ b/cpp/base/device_logger.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -20,6 +20,11 @@ void DeviceLogger::Debug(const string &message) const Log(level::debug, message); } +void DeviceLogger::Info(const string &message) const +{ + Log(level::info, message); +} + void DeviceLogger::Warn(const string &message) const { Log(level::warn, message); diff --git a/cpp/base/device_logger.h b/cpp/base/device_logger.h index fc374ae4..cd92f9d0 100644 --- a/cpp/base/device_logger.h +++ b/cpp/base/device_logger.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -19,6 +19,7 @@ class DeviceLogger void Trace(const string&) const; void Debug(const string&) const; + void Info(const string&) const; void Warn(const string&) const; void Error(const string&) const; diff --git a/cpp/base/interfaces/scsi_block_commands.h b/cpp/base/interfaces/scsi_block_commands.h index cec4ff3e..af0ab7dc 100644 --- a/cpp/base/interfaces/scsi_block_commands.h +++ b/cpp/base/interfaces/scsi_block_commands.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2022 Uwe Seimet // diff --git a/cpp/base/interfaces/scsi_communications_commands.h b/cpp/base/interfaces/scsi_communications_commands.h index 85589544..6029e364 100644 --- a/cpp/base/interfaces/scsi_communications_commands.h +++ b/cpp/base/interfaces/scsi_communications_commands.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2024 Uwe Seimet // diff --git a/cpp/base/interfaces/scsi_mmc_commands.h b/cpp/base/interfaces/scsi_mmc_commands.h index d498f655..3661386d 100644 --- a/cpp/base/interfaces/scsi_mmc_commands.h +++ b/cpp/base/interfaces/scsi_mmc_commands.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2022 Uwe Seimet // diff --git a/cpp/base/interfaces/scsi_primary_commands.h b/cpp/base/interfaces/scsi_primary_commands.h index fd0eb740..caabd3bd 100644 --- a/cpp/base/interfaces/scsi_primary_commands.h +++ b/cpp/base/interfaces/scsi_primary_commands.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2022 Uwe Seimet // diff --git a/cpp/base/interfaces/scsi_printer_commands.h b/cpp/base/interfaces/scsi_printer_commands.h index f834074d..0fe915b2 100644 --- a/cpp/base/interfaces/scsi_printer_commands.h +++ b/cpp/base/interfaces/scsi_printer_commands.h @@ -1,8 +1,8 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // -// Copyright (C) 2021-2022 Uwe Seimet +// Copyright (C) 2021-2024 Uwe Seimet // // Interface for SCSI printer commands (see SCSI-2 specification) // @@ -20,7 +20,6 @@ class ScsiPrinterCommands // Mandatory commands virtual void Print() = 0; - virtual void ReleaseUnit() = 0; - virtual void ReserveUnit() = 0; - virtual void SendDiagnostic() = 0; + + // ReleaseUnit(), ReserveUnit() and SendDiagnostic() are contributed by PrimaryDevice }; diff --git a/cpp/base/memory_util.cpp b/cpp/base/memory_util.cpp index d013a536..c569c0a7 100644 --- a/cpp/base/memory_util.cpp +++ b/cpp/base/memory_util.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2023 Uwe Seimet // diff --git a/cpp/base/memory_util.h b/cpp/base/memory_util.h index 4fee2060..cfa81f3c 100644 --- a/cpp/base/memory_util.h +++ b/cpp/base/memory_util.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // diff --git a/cpp/base/primary_device.cpp b/cpp/base/primary_device.cpp index 1b3efb74..d182050a 100644 --- a/cpp/base/primary_device.cpp +++ b/cpp/base/primary_device.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -12,6 +12,7 @@ #include "primary_device.h" using namespace memory_util; +using namespace s2p_util; bool PrimaryDevice::Init(const param_map ¶ms) { @@ -109,7 +110,7 @@ void PrimaryDevice::Inquiry() throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb); } - const vector buf = InquiryInternal(); + const vector &buf = InquiryInternal(); const size_t allocation_length = min(buf.size(), static_cast(GetInt16(GetController()->GetCdb(), 3))); @@ -117,6 +118,7 @@ void PrimaryDevice::Inquiry() // Report if the device does not support the requested LUN if (!GetController()->GetDeviceForLun(GetController()->GetEffectiveLun())) { + // SCSI-2 section 8.2.5.1: Incorrect logical unit handling GetController()->GetBuffer().data()[0] = 0x7f; } @@ -152,7 +154,7 @@ void PrimaryDevice::RequestSense() { int effective_lun = GetController()->GetEffectiveLun(); - // Note: According to the SCSI specs the LUN handling for REQUEST SENSE non-existing LUNs do *not* result + // According to the specification the LUN handling for REQUEST SENSE for non-existing LUNs does not result // in CHECK CONDITION. Only the Sense Key and ASC are set in order to signal the non-existing LUN. if (!GetController()->GetDeviceForLun(effective_lun)) { // LUN 0 can be assumed to be present (required to call RequestSense() below) @@ -164,7 +166,7 @@ void PrimaryDevice::RequestSense() GetController()->Error(sense_key::illegal_request, asc::invalid_lun, status::good); } - vector buf = GetController()->GetDeviceForLun(effective_lun)->HandleRequestSense(); + const vector &buf = GetController()->GetDeviceForLun(effective_lun)->HandleRequestSense(); const auto length = static_cast(min(buf.size(), static_cast(GetController()->GetCdbByte(4)))); GetController()->CopyToBuffer(buf.data(), length); @@ -207,14 +209,8 @@ void PrimaryDevice::CheckReady() vector PrimaryDevice::HandleInquiry(device_type type, bool is_removable) const { - vector buf(0x1F + 5); - - // Basic data - // buf[0] ... SCSI device type - // buf[1] ... Bit 7: Removable/not removable - // buf[2] ... SCSI compliance level of command system - // buf[3] ... SCSI compliance level of Inquiry response - // buf[4] ... Inquiry additional data + vector buf(0x1f + 5); + buf[0] = static_cast(type); buf[1] = is_removable ? 0x80 : 0x00; buf[2] = static_cast(level); @@ -235,20 +231,17 @@ vector PrimaryDevice::HandleRequestSense() const throw scsi_exception(sense_key::not_ready, asc::medium_not_present); } - // Set 18 bytes including extended sense data - + // 18 bytes including extended sense data vector buf(18); // Current error buf[0] = (byte)0x70; - buf[2] = (byte)(sense_key); - buf[7] = (byte)10; - buf[12] = (byte)(asc); + buf[2] = (byte)sense_key; + buf[7] = byte { 10 }; + buf[12] = (byte)asc; - LogTrace( - fmt::format("Status ${0:02x}, Sense Key ${1:02x}, ASC ${2:02x}", static_cast(GetController()->GetStatus()), - static_cast(buf[2]), static_cast(buf[12]))); + LogTrace(fmt::format("{0}: {1}", STATUS_MAPPING.at(GetController()->GetStatus()), FormatSenseData(sense_key, asc))); return buf; } @@ -257,43 +250,31 @@ void PrimaryDevice::ReserveUnit() { reserving_initiator = GetController()->GetInitiatorId(); - if (reserving_initiator != -1) { - LogTrace(fmt::format("Reserved device for initiator ID {}", reserving_initiator)); - } - else { - LogTrace("Reserved device for unknown initiator"); - } - StatusPhase(); } void PrimaryDevice::ReleaseUnit() { - if (reserving_initiator != -1) { - LogTrace(fmt::format("Released device reserved by initiator ID {}", reserving_initiator)); - } - else { - LogTrace("Released device reserved by unknown initiator"); - } - DiscardReservation(); StatusPhase(); } -bool PrimaryDevice::CheckReservation(int initiator_id, scsi_command cmd, bool prevent_removal) const +bool PrimaryDevice::CheckReservation(int initiator_id) const { if (reserving_initiator == NOT_RESERVED || reserving_initiator == initiator_id) { return true; } // A reservation is valid for all commands except those excluded below + const auto cmd = GetController()->GetOpcode(); if (cmd == scsi_command::cmd_inquiry || cmd == scsi_command::cmd_request_sense || cmd == scsi_command::cmd_release6) { return true; } + // PREVENT ALLOW MEDIUM REMOVAL is permitted if the prevent bit is 0 - if (cmd == scsi_command::cmd_prevent_allow_medium_removal && !prevent_removal) { + if (cmd == scsi_command::cmd_prevent_allow_medium_removal && !(GetController()->GetCdbByte(4) & 0x01)) { return true; } @@ -304,6 +285,9 @@ bool PrimaryDevice::CheckReservation(int initiator_id, scsi_command cmd, bool pr LogTrace("Unknown initiator tries to access reserved device"); } + GetController()->Error(sense_key::aborted_command, asc::no_additional_sense_information, + status::reservation_conflict); + return false; } diff --git a/cpp/base/primary_device.h b/cpp/base/primary_device.h index 5baca989..86029c23 100644 --- a/cpp/base/primary_device.h +++ b/cpp/base/primary_device.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -62,7 +62,7 @@ class PrimaryDevice : private ScsiPrimaryCommands, public Device return delay_after_bytes; } - bool CheckReservation(int, scsi_command, bool) const; + bool CheckReservation(int) const; void DiscardReservation(); void Reset() override; @@ -145,6 +145,10 @@ class PrimaryDevice : private ScsiPrimaryCommands, public Device { device_logger.Debug(s); } + void LogInfo(const string &s) const + { + device_logger.Info(s); + } void LogWarn(const string &s) const { device_logger.Warn(s); @@ -156,7 +160,7 @@ class PrimaryDevice : private ScsiPrimaryCommands, public Device private: - static const int NOT_RESERVED = -2; + static constexpr int NOT_RESERVED = -2; void SetController(AbstractController*); diff --git a/cpp/base/property_handler.cpp b/cpp/base/property_handler.cpp index 6e1e1513..35494837 100644 --- a/cpp/base/property_handler.cpp +++ b/cpp/base/property_handler.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2024 Uwe Seimet // @@ -127,9 +127,9 @@ map> PropertyHandler::GetCustomModePages(const string &vendor, continue; } - int page; - if (!GetAsUnsignedInt(key_components[1], page) || page > 0x3e) { - warn("Ignored invalid mode page property '{}'", key); + int page_code; + if (!GetAsUnsignedInt(key_components[1], page_code) || page_code > 0x3e) { + warn("Ignored invalid page code in mode page property '{}'", key); continue; } @@ -138,35 +138,34 @@ map> PropertyHandler::GetCustomModePages(const string &vendor, continue; } - vector data; + vector page_data; try { - data = HexToBytes(value); + page_data = HexToBytes(value); } - catch (const parser_exception&) { - warn("Ignored invalid mode page definition for page {0}: {1}", page, value); + catch (const out_of_range&) { + warn("Ignored invalid mode page definition for page {0}: {1}", page_code, value); continue; } - if (data.empty()) { - trace("Removing default mode page {}", page); + if (page_data.empty()) { + trace("Removing default mode page {}", page_code); } else { // Validate the page code and (except for page 0, which has no well-defined format) the page size - if (page != (static_cast(data[0]) & 0x3f)) { - warn("Ignored mode page definition with inconsistent page codes {0}: {1}", page, data[0]); + if (page_code != (static_cast(page_data[0]) & 0x3f)) { + warn("Ignored mode page definition with inconsistent page code {0}: {1}", page_code, page_data[0]); continue; - } - if (page && static_cast(data.size() - 2) != data[1]) { - warn("Ignored mode page definition with wrong page size {0}: {1}", page, data[1]); + if (page_code && static_cast(page_data.size() - 2) != page_data[1]) { + warn("Ignored mode page definition with wrong page size {0}: {1}", page_code, page_data[1]); continue; } - trace("Adding/replacing mode page {0}: {1}", page, key_components[2]); + trace("Adding/replacing mode page {0}: {1}", page_code, value); } - pages[page] = data; + pages[page_code] = page_data; } return pages; diff --git a/cpp/base/property_handler.h b/cpp/base/property_handler.h index 0e81ed62..7912e48e 100644 --- a/cpp/base/property_handler.h +++ b/cpp/base/property_handler.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2024 Uwe Seimet // diff --git a/cpp/buses/bus.cpp b/cpp/buses/bus.cpp index 8b1fb3c8..549bd214 100644 --- a/cpp/buses/bus.cpp +++ b/cpp/buses/bus.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) // Copyright (C) 2014-2020 GIMONS @@ -8,12 +8,303 @@ // //--------------------------------------------------------------------------- -#include -#include "bus.h" +#include +#include +#include "bus_factory.h" -using namespace std; using namespace scsi_defs; +bool Bus::Init(bool mode) +{ + static_assert(SIGNAL_CONTROL_MODE == 0 || SIGNAL_CONTROL_MODE == 2); + + target_mode = mode; + + return true; +} + +int Bus::CommandHandShake(vector &buf) +{ + DisableIRQ(); + + SetREQ(true); + + bool ack = WaitSignal(PIN_ACK, true); + + WaitBusSettle(); + + buf[0] = GetDAT(); + + SetREQ(false); + + // Timeout waiting for ACK to change + if (!ack || !WaitSignal(PIN_ACK, false)) { + EnableIRQ(); + return 0; + } + + // The ICD AdSCSI ST, AdSCSI Plus ST and AdSCSI Micro ST host adapters allow SCSI devices to be connected + // to the ACSI bus of Atari ST/TT computers and some clones. ICD-awarerrore drivers prepend a $1F byte in front + // of the CDB (effectively resulting in a custom SCSI command) in order to get access to the full SCSI + // command set. Native ACSI is limited to the low SCSI command classes with command bytes < $20. + // Most other host adapters (e.g. LINK96/97 and the one by Inventronik) and also several devices (e.g. + // UltraSatan or GigaFile) that can directly be connected to the Atari's ACSI port also support ICD + // semantics. In fact, these semantics have become a standard in the Atari world. + // SCSi2Pi becomes ICD compatible by ignoring the prepended $1F byte before processing the CDB. + if (buf[0] == 0x1f) { + SetREQ(true); + + ack = WaitSignal(PIN_ACK, true); + + WaitBusSettle(); + + // Get the actual SCSI command + buf[0] = GetDAT(); + + SetREQ(false); + + // Timeout waiting for ACK to change + if (!ack || !WaitSignal(PIN_ACK, false)) { + EnableIRQ(); + return 0; + } + } + + const int command_byte_count = BusFactory::Instance().GetCommandBytesCount(static_cast(buf[0])); + if (!command_byte_count) { + EnableIRQ(); + + // Unknown command + return 0; + } + + int offset = 0; + + int bytes_received; + for (bytes_received = 1; bytes_received < command_byte_count; bytes_received++) { + ++offset; + + SetREQ(true); + + ack = WaitSignal(PIN_ACK, true); + + WaitBusSettle(); + + buf[offset] = GetDAT(); + + SetREQ(false); + + // Timeout waiting for ACK to change + if (!ack || !WaitSignal(PIN_ACK, false)) { + break; + } + } + + EnableIRQ(); + + return bytes_received; +} + +// Initiator MESSAGE IN +int Bus::MsgInHandShake() +{ + const phase_t phase = GetPhase(); + + if (!WaitSignal(PIN_REQ, true)) { + return -1; + } + + // Phase error + if (GetPhase() != phase) { + return -1; + } + + DisableIRQ(); + WaitBusSettle(); + EnableIRQ(); + + const int msg = GetDAT(); + + SetACK(true); + + // Request MESSAGE OUT phase for rejecting any unsupported message (only COMMAND COMPLETE is supported) + if (msg) { + SetATN(true); + } + + WaitSignal(PIN_REQ, false); + + SetACK(false); + + return msg; +} + +// Handshake for DATA IN and target MESSAGE IN +int Bus::ReceiveHandShake(uint8_t *buf, int count) +{ + int bytes_received; + + DisableIRQ(); + + if (target_mode) { + for (bytes_received = 0; bytes_received < count; bytes_received++) { + SetREQ(true); + + const bool ack = WaitSignal(PIN_ACK, true); + + WaitBusSettle(); + + *buf = GetDAT(); + + SetREQ(false); + + // Timeout waiting for ACK to change + if (!ack || !WaitSignal(PIN_ACK, false)) { + break; + } + + buf++; + } + } else { + const phase_t phase = GetPhase(); + + for (bytes_received = 0; bytes_received < count; bytes_received++) { + if (!WaitSignal(PIN_REQ, true)) { + break; + } + + // Phase error + if (GetPhase() != phase) { + break; + } + + WaitBusSettle(); + + *buf = GetDAT(); + + SetACK(true); + + const bool req = WaitSignal(PIN_REQ, false); + + SetACK(false); + + if (!req || GetPhase() != phase) { + break; + } + + buf++; + } + } + + EnableIRQ(); + + return bytes_received; +} + +// Handshake for DATA OUT and MESSAGE OUT +#ifdef BUILD_SCDP +int Bus::SendHandShake(const uint8_t *buf, int count, int daynaport_delay_after_bytes) +#else +int Bus::SendHandShake(const uint8_t *buf, int count, int) +#endif +{ + int bytes_sent; + + DisableIRQ(); + + if (target_mode) { + for (bytes_sent = 0; bytes_sent < count; bytes_sent++) { +#ifdef BUILD_SCDP + if (bytes_sent == daynaport_delay_after_bytes) { + const timespec ts = { .tv_sec = 0, .tv_nsec = SCSI_DELAY_SEND_DATA_DAYNAPORT_NS }; + EnableIRQ(); + nanosleep(&ts, nullptr); + DisableIRQ(); + } +#endif + + SetDAT(*buf); + + if (!WaitSignal(PIN_ACK, false)) { + break; + } + + SetREQ(true); + + const bool ack = WaitSignal(PIN_ACK, true); + + SetREQ(false); + + if (!ack) { + break; + } + + buf++; + } + + WaitSignal(PIN_ACK, false); + } else { + const phase_t phase = GetPhase(); + + for (bytes_sent = 0; bytes_sent < count; bytes_sent++) { + SetDAT(*buf); + + if (!WaitSignal(PIN_REQ, true)) { + break; + } + + // Signal the last MESSAGE OUT byte + if (phase == phase_t::msgout && bytes_sent == count - 1) { + SetATN(false); + } + + // Phase error + if (GetPhase() != phase) { + break; + } + + SetACK(true); + + const bool req = WaitSignal(PIN_REQ, false); + + SetACK(false); + + if (!req || GetPhase() != phase) { + break; + } + + buf++; + } + } + + EnableIRQ(); + + return bytes_sent; +} + +bool Bus::WaitSignal(int pin, bool state) +{ + const auto now = chrono::steady_clock::now(); + + // Wait for up to 3 s + do { + Acquire(); + + if (GetSignal(pin) == state) { + return true; + } + + if (GetRST()) { + spdlog::warn("Received RST signal during {} phase, aborting", GetPhaseName(GetPhase())); + return false; + } + } while ((chrono::duration_cast < chrono::seconds > (chrono::steady_clock::now() - now).count()) < 3); + + spdlog::trace("Timeout while waiting for ACK/REQ to change to {}", state ? "true" : "false"); + + return false; +} + phase_t Bus::GetPhase() { Acquire(); @@ -26,22 +317,11 @@ phase_t Bus::GetPhase() return phase_t::busfree; } - // Get target phase from bus signal lines - int mci = GetMSG() ? 0b100 : 0b000; - mci |= GetCD() ? 0b010 : 0b000; - mci |= GetIO() ? 0b001 : 0b000; - return GetPhase(mci); -} - -string Bus::GetPhaseName(phase_t phase) -{ - assert(phase_names.find(phase) != phase_names.end()); - return phase_names.at(phase); + // Get phase from bus signal lines + return phases[(GetMSG() ? 0b100 : 0b000) | (GetCD() ? 0b010 : 0b000) | (GetIO() ? 0b001 : 0b000)]; } -// Phase Table -// Reference Table 8: https://www.staff.uni-mainz.de/tacke/scsi/SCSI2-06.html -// This determines the phase based upon the Msg, C/D and I/O signals. +// Phase Table with the phases based upon the MSG, C/D and I/O signals // // |MSG|C/D|I/O| Phase // | 0 | 0 | 0 | DATA OUT @@ -53,7 +333,7 @@ string Bus::GetPhaseName(phase_t phase) // | 1 | 1 | 0 | MESSAGE OUT // | 1 | 1 | 1 | MESSAGE IN // -const array Bus::phases = { +constexpr array Bus::phases = { phase_t::dataout, phase_t::datain, phase_t::command, @@ -64,16 +344,16 @@ const array Bus::phases = { phase_t::msgin }; -const unordered_map Bus::phase_names = { - { phase_t::busfree, "BUS FREE" }, - { phase_t::arbitration, "ARBITRATION" }, - { phase_t::selection, "SELECTION" }, - { phase_t::reselection, "RESELECTION" }, - { phase_t::command, "COMMAND" }, - { phase_t::datain, "DATA IN" }, - { phase_t::dataout, "DATA OUT" }, - { phase_t::status, "STATUS" }, - { phase_t::msgin, "MESSAGE IN" }, - { phase_t::msgout, "MESSAGE OUT" }, - { phase_t::reserved, "reserved" } +const array Bus::phase_names = { + "BUS FREE", + "ARBITRATION", + "SELECTION", + "RESELECTION", + "COMMAND", + "DATA IN", + "DATA OUT", + "STATUS", + "MESSAGE IN", + "MESSAGE OUT", + "???" }; diff --git a/cpp/buses/bus.h b/cpp/buses/bus.h index 3450a630..83555c8c 100644 --- a/cpp/buses/bus.h +++ b/cpp/buses/bus.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) // Copyright (C) 2014-2020 GIMONS @@ -10,51 +10,249 @@ #pragma once -#include -#include +#include #include -#include "pin_control.h" #include "shared/scsi.h" -using namespace std; +#if defined BOARD_STANDARD +#include "buses/connection_type/connection_standard.h" +#elif defined BOARD_FULLSPEC +#include "buses/connection_type/connection_fullspec.h" +#elif defined BOARD_AIBOM +#include "buses/connection_type/connection_aibom.h" +#elif defined BOARD_GAMERNIUM +#include "buses/connection_type/connection_gamernium.h" +#else +#error Invalid connection type or none specified +#endif + using namespace scsi_defs; -class Bus : public PinControl +//--------------------------------------------------------------------------- +// +// SIGNAL_CONTROL_MODE: Signal control mode selection +// You can customize the signal control logic from Version 1.22 +// +// 0: SCSI logical specification +// Conversion board using 74LS641-1 etc. directly connected or published on HP +// True : 0V +// False : Open collector output (disconnect from bus) +// +// 1: Negative logic specification (when using conversion board for negative logic -> SCSI logic) +// There is no conversion board with this specification at this time +// True : 0V -> (CONVERT) -> 0V +// False : 3.3V -> (CONVERT) -> Open collector output +// +// 2: Positive logic specification (when using the conversion board for positive logic -> SCSI logic) +// PiSCSI Adapter Rev.C @132sync etc. +// +// True : 3.3V -> (CONVERT) -> 0V +// False : 0V -> (CONVERT) -> Open collector output +// +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// +// Control signal pin assignment setting +// GPIO pin mapping table for control signals. +// +// Control signal: +// PIN_ACT +// Signal that indicates the status of processing SCSI command. +// PIN_ENB +// Signal that indicates the valid signal from start to finish. +// PIN_TAD +// Signal that indicates the input/output direction of the target signal (BSY,IO,CD,MSG,REG). +// PIN_IND +// Signal that indicates the input/output direction of the initiator signal (SEL, ATN, RST, ACK). +// PIN_DTD +// Signal that indicates the input/output direction of the data lines (DT0...DT7,DP). +// +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// +// Control signal output logic +// 0V:FALSE 3.3V:TRUE +// +// ACT_ON +// PIN_ACT signal +// ENB_ON +// PIN_ENB signal +// TAD_IN +// PIN_TAD This is the logic when inputting. +// IND_IN +// PIN_ENB This is the logic when inputting. +// DTD_IN +// PIN_ENB This is the logic when inputting. +// +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// +// SCSI signal pin assignment setting +// GPIO pin mapping table for SCSI signals. +// PIN_DT0~PIN_SEL +// +//--------------------------------------------------------------------------- + +// Constant declarations (GPIO) +constexpr static int GPIO_INPUT = 0; +constexpr static int GPIO_OUTPUT = 1; +constexpr static int GPIO_PULLNONE = 0; +constexpr static int GPIO_PULLDOWN = 1; + +// Constant declarations (Control signals) +#define ACT_OFF !ACT_ON +#define ENB_OFF !ENB_ON +#define TAD_OUT !TAD_IN +#define IND_OUT !IND_IN +#define DTD_OUT !DTD_IN + +// Constant declarations (SCSI) +constexpr static int IN = GPIO_INPUT; +constexpr static int OUT = GPIO_OUTPUT; +constexpr static int ON = 1; +constexpr static int OFF = 0; + +class Bus { public: - virtual bool Init(bool) = 0; + Bus() = default; + virtual ~Bus() = default; + + virtual bool Init(bool = true); virtual void Reset() = 0; virtual void CleanUp() = 0; + virtual uint32_t Acquire() = 0; + + virtual bool WaitForSelection() = 0; + + virtual void SetBSY(bool) = 0; + + virtual void SetSEL(bool) = 0; + + virtual bool GetIO() = 0; + virtual void SetIO(bool) = 0; + + virtual uint8_t GetDAT() = 0; + virtual void SetDAT(uint8_t) = 0; + + virtual bool GetSignal(int) const = 0; + virtual void SetSignal(int, bool) = 0; + + virtual bool WaitSignal(int, bool); + + int CommandHandShake(vector&); + int MsgInHandShake(); + int ReceiveHandShake(uint8_t*, int); + int SendHandShake(const uint8_t*, int, int = SEND_NO_DELAY); + + bool GetBSY() const + { + return GetSignal(PIN_BSY); + } + + bool GetSEL() const + { + return GetSignal(PIN_SEL); + } + + inline bool GetREQ() const + { + return GetSignal(PIN_REQ); + } + + inline void SetREQ(bool state) + { + SetSignal(PIN_REQ, state); + } + + bool GetATN() const + { + return GetSignal(PIN_ATN); + } + + void SetATN(bool state) + { + SetSignal(PIN_ATN, state); + } + + inline bool GetACK() const + { + return GetSignal(PIN_ACK); + } + + inline void SetACK(bool state) + { + SetSignal(PIN_ACK, state); + } + + inline bool GetRST() const + { + return GetSignal(PIN_RST); + } + + void SetRST(bool state) + { + SetSignal(PIN_RST, state); + } + + inline bool GetMSG() const + { + return GetSignal(PIN_MSG); + } + + void SetMSG(bool state) + { + SetSignal(PIN_MSG, state); + } + + inline bool GetCD() const + { + return GetSignal(PIN_CD); + } + + void SetCD(bool state) + { + SetSignal(PIN_CD, state); + } + phase_t GetPhase(); - static phase_t GetPhase(int mci) + + static string GetPhaseName(phase_t phase) { - return phases[mci]; + return phase_names[static_cast(phase)]; } - static string GetPhaseName(phase_t); - virtual uint32_t Acquire() = 0; - virtual int CommandHandShake(vector&) = 0; - virtual int MsgInHandShake() = 0; - virtual int ReceiveHandShake(uint8_t*, int) = 0; - virtual int SendHandShake(uint8_t*, int, int = SEND_NO_DELAY) = 0; + // For work-around required by the DaynaPort emulation + static constexpr int SEND_NO_DELAY = -1; - virtual bool WaitREQ(bool) = 0; - virtual bool WaitACK(bool) = 0; +protected: - virtual bool WaitForSelection() = 0; + virtual void WaitBusSettle() const = 0; - virtual bool GetSignal(int) const = 0; - virtual void SetSignal(int, bool) = 0; + virtual void EnableIRQ() = 0; + virtual void DisableIRQ() = 0; - // Work-around needed for the DaynaPort emulation - static const int SEND_NO_DELAY = -1; + inline bool IsTarget() const + { + return target_mode; + } private: static const array phases; - static const unordered_map phase_names; + static const array phase_names; + + bool target_mode = true; + + // The DaynaPort SCSI Link do a short delay in the middle of transfering + // a packet. This is the number of ns that will be delayed between the + // header and the actual data. + static constexpr int SCSI_DELAY_SEND_DATA_DAYNAPORT_NS = 100'000; }; diff --git a/cpp/buses/bus_factory.cpp b/cpp/buses/bus_factory.cpp index 7eb3c877..e883d312 100644 --- a/cpp/buses/bus_factory.cpp +++ b/cpp/buses/bus_factory.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2023-2024 Uwe Seimet // @@ -9,7 +9,6 @@ #include #include #include -#include "rpi_bus.h" #include "in_process_bus.h" #include "bus_factory.h" @@ -76,7 +75,7 @@ unique_ptr BusFactory::CreateBus(bool target, bool in_process) bus = make_unique(InProcessBus::Instance(), in_process); } else { - bus = make_unique(); + bus = make_unique(pi_type); } if (bus && bus->Init(target)) { @@ -99,13 +98,19 @@ bool BusFactory::CheckForPi() s << in.rdbuf(); const string &model = s.str(); - if (model.starts_with("Raspberry Pi") && !model.starts_with("Raspberry Pi 5")) { - is_raspberry_pi = true; - return true; + if (!model.starts_with("Raspberry Pi ") || model.size() < 13) { + warn("This platform is not a Raspberry Pi, functionality is limited"); + return false; + } + + const int type = model.find("Zero") != string::npos ? 1 : model.substr(13, 1)[0] - '0'; + if (type <= 0 || type > 4) { + warn("Unsupported Raspberry Pi model '{}', functionality is limited", model); + return false; } - warn("Unsupported Raspberry Pi model '{}', functionality is limited", model); + pi_type = static_cast(type); - return false; + return true; } diff --git a/cpp/buses/bus_factory.h b/cpp/buses/bus_factory.h index c8ddbe88..3b3819a6 100644 --- a/cpp/buses/bus_factory.h +++ b/cpp/buses/bus_factory.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2023-2024 Uwe Seimet // @@ -8,7 +8,8 @@ #pragma once -#include "bus.h" +#include +#include "rpi_bus.h" using namespace std; @@ -37,7 +38,7 @@ class BusFactory bool IsRaspberryPi() const { - return is_raspberry_pi; + return pi_type != RpiBus::PiType::unknown; } private: @@ -48,7 +49,7 @@ class BusFactory bool CheckForPi(); - bool is_raspberry_pi = false; + RpiBus::PiType pi_type = RpiBus::PiType::unknown; array command_byte_counts; diff --git a/cpp/buses/connection_type/connection_aibom.h b/cpp/buses/connection_type/connection_aibom.h index b49923c8..54e90edc 100644 --- a/cpp/buses/connection_type/connection_aibom.h +++ b/cpp/buses/connection_type/connection_aibom.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Powered by XM6 TypeG Technology. // Copyright (C) 2016-2020 GIMONS diff --git a/cpp/buses/connection_type/connection_fullspec.h b/cpp/buses/connection_type/connection_fullspec.h index c61bbae0..4446b6e5 100644 --- a/cpp/buses/connection_type/connection_fullspec.h +++ b/cpp/buses/connection_type/connection_fullspec.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Powered by XM6 TypeG Technology. // Copyright (C) 2016-2020 GIMONS diff --git a/cpp/buses/connection_type/connection_gamernium.h b/cpp/buses/connection_type/connection_gamernium.h index 975e5f5f..082c6e80 100644 --- a/cpp/buses/connection_type/connection_gamernium.h +++ b/cpp/buses/connection_type/connection_gamernium.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Powered by XM6 TypeG Technology. // Copyright (C) 2016-2020 GIMONS diff --git a/cpp/buses/connection_type/connection_standard.h b/cpp/buses/connection_type/connection_standard.h index 16b7a429..88cc81fe 100644 --- a/cpp/buses/connection_type/connection_standard.h +++ b/cpp/buses/connection_type/connection_standard.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Powered by XM6 TypeG Technology. // Copyright (C) 2016-2020 GIMONS diff --git a/cpp/buses/gpio_bus.cpp b/cpp/buses/gpio_bus.cpp deleted file mode 100644 index 90215f06..00000000 --- a/cpp/buses/gpio_bus.cpp +++ /dev/null @@ -1,302 +0,0 @@ -//--------------------------------------------------------------------------- -// -// SCSI target emulator and SCSI tools for the Raspberry Pi -// -// Copyright (C) 2016-2020 GIMONS -// Copyright (C) 2023-2024 Uwe Seimet -// -//--------------------------------------------------------------------------- - -#include -#include "bus_factory.h" -#include "gpio_bus.h" - -using namespace std; - -bool GpioBus::Init(bool t) -{ - target_mode = t; - - return true; -} - -int GpioBus::CommandHandShake(vector &buf) -{ - DisableIRQ(); - - SetREQ(true); - - bool ack = WaitACK(true); - - WaitBusSettle(); - - buf[0] = GetDAT(); - - SetREQ(false); - - // Timeout waiting for ACK to change - if (!ack || !WaitACK(false)) { - EnableIRQ(); - return 0; - } - - // The ICD AdSCSI ST, AdSCSI Plus ST and AdSCSI Micro ST host adapters allow SCSI devices to be connected - // to the ACSI bus of Atari ST/TT computers and some clones. ICD-awarerrore drivers prepend a $1F byte in front - // of the CDB (effectively resulting in a custom SCSI command) in order to get access to the full SCSI - // command set. Native ACSI is limited to the low SCSI command classes with command bytes < $20. - // Most other host adapters (e.g. LINK96/97 and the one by Inventronik) and also several devices (e.g. - // UltraSatan or GigaFile) that can directly be connected to the Atari's ACSI port also support ICD - // semantics. In fact, these semantics have become a standard in the Atari world. - // SCSi2Pi becomes ICD compatible by ignoring the prepended $1F byte before processing the CDB. - if (buf[0] == 0x1f) { - SetREQ(true); - - ack = WaitACK(true); - - WaitBusSettle(); - - // Get the actual SCSI command - buf[0] = GetDAT(); - - SetREQ(false); - - // Timeout waiting for ACK to change - if (!ack || !WaitACK(false)) { - EnableIRQ(); - return 0; - } - } - - const int command_byte_count = BusFactory::Instance().GetCommandBytesCount(static_cast(buf[0])); - if (!command_byte_count) { - EnableIRQ(); - - // Unknown command - return 0; - } - - int offset = 0; - - int bytes_received; - for (bytes_received = 1; bytes_received < command_byte_count; bytes_received++) { - ++offset; - - SetREQ(true); - - ack = WaitACK(true); - - WaitBusSettle(); - - buf[offset] = GetDAT(); - - SetREQ(false); - - // Timeout waiting for ACK to change - if (!ack || !WaitACK(false)) { - break; - } - } - - EnableIRQ(); - - return bytes_received; -} - -// Initiator MESSAGE IN -int GpioBus::MsgInHandShake() -{ - const phase_t phase = GetPhase(); - - // Check for timeout waiting for REQ signal - if (!WaitREQ(true)) { - return -1; - } - - // Phase error - if (GetPhase() != phase) { - return -1; - } - - DisableIRQ(); - WaitBusSettle(); - EnableIRQ(); - - const uint8_t msg = GetDAT(); - - SetACK(true); - - // Request MESSAGE OUT phase for rejecting any unsupported message (only COMMAND COMPLETE is supported) - if (msg) { - SetATN(true); - } - - WaitREQ(false); - - SetACK(false); - - return msg; -} - -// Handshake for DATA IN and target MESSAGE IN -int GpioBus::ReceiveHandShake(uint8_t *buf, int count) -{ - int bytes_received; - - DisableIRQ(); - - if (target_mode) { - for (bytes_received = 0; bytes_received < count; bytes_received++) { - SetREQ(true); - - const bool ack = WaitACK(true); - - WaitBusSettle(); - - *buf = GetDAT(); - - SetREQ(false); - - // Timeout waiting for ACK to change - if (!ack || !WaitACK(false)) { - break; - } - - buf++; - } - } else { - const phase_t phase = GetPhase(); - - for (bytes_received = 0; bytes_received < count; bytes_received++) { - // Check for timeout waiting for REQ signal - if (!WaitREQ(true)) { - break; - } - - // Phase error - if (GetPhase() != phase) { - break; - } - - WaitBusSettle(); - - *buf = GetDAT(); - - SetACK(true); - - const bool req = WaitREQ(false); - - SetACK(false); - - // Check for timeout waiting for REQ to clear and for unexpected phase change - if (!req || GetPhase() != phase) { - break; - } - - buf++; - } - } - - EnableIRQ(); - - return bytes_received; -} - -// Handshake for DATA OUT and MESSAGE OUT -int GpioBus::SendHandShake(uint8_t *buf, int count, int daynaport_delay_after_bytes) -{ - int bytes_sent; - - DisableIRQ(); - - if (target_mode) { - for (bytes_sent = 0; bytes_sent < count; bytes_sent++) { - if (bytes_sent == daynaport_delay_after_bytes) { - EnableIRQ(); - const timespec ts = { .tv_sec = 0, .tv_nsec = SCSI_DELAY_SEND_DATA_DAYNAPORT_NS }; - nanosleep(&ts, nullptr); - DisableIRQ(); - } - - SetDAT(*buf); - - // Check for timeout waiting for ACK to clear - if (!WaitACK(false)) { - break; - } - - SetREQ(true); - - const bool ack = WaitACK(true); - - SetREQ(false); - - // Check for timeout waiting for ACK to clear - if (!ack) { - break; - } - - buf++; - } - - WaitACK(false); - } else { - const phase_t phase = GetPhase(); - - for (bytes_sent = 0; bytes_sent < count; bytes_sent++) { - SetDAT(*buf); - - // Check for timeout waiting for REQ to be asserted - if (!WaitREQ(true)) { - break; - } - - // Signal the last MESSAGE OUT byte - if (phase == phase_t::msgout && bytes_sent == count - 1) { - SetATN(false); - } - - // Phase error - if (GetPhase() != phase) { - break; - } - - SetACK(true); - - const bool req = WaitREQ(false); - - SetACK(false); - - // Check for timeout waiting for REQ to clear and for unexpected phase change - if (!req || GetPhase() != phase) { - break; - } - - buf++; - } - } - - EnableIRQ(); - - return bytes_sent; -} - -bool GpioBus::WaitSignal(int pin, bool state) -{ - const auto now = chrono::steady_clock::now(); - - // Wait for up to 3 s - do { - Acquire(); - - if (GetSignal(pin) == state) { - return true; - } - - // Abort on a reset - if (GetRST()) { - return false; - } - } while ((chrono::duration_cast(chrono::steady_clock::now() - now).count()) < 3); - - return false; -} diff --git a/cpp/buses/gpio_bus.h b/cpp/buses/gpio_bus.h deleted file mode 100644 index 2c761ed4..00000000 --- a/cpp/buses/gpio_bus.h +++ /dev/null @@ -1,164 +0,0 @@ -//--------------------------------------------------------------------------- -// -// SCSI target emulator and SCSI tools for the Raspberry Pi -// -// Copyright (C) 2016-2020 GIMONS -// Copyright (C) 2023-2024 Uwe Seimet -// -//--------------------------------------------------------------------------- - -#pragma once - -#include "buses/bus.h" - -//--------------------------------------------------------------------------- -// -// Connection method definitions -// -//--------------------------------------------------------------------------- -//#define BOARD_STANDARD // Standard (SCSI logic, standard pin assignment) -//#define BOARD_FULLSPEC // Full spec (SCSI logic, standard pin assignment) -//#define BOARD_AIBOM // AIBOM version (positive logic, unique pin assignment) -//#define BOARD_GAMERNIUM // GAMERnium.com version (standard logic, unique pin assignment) - -#if defined BOARD_STANDARD -#include "buses/connection_type/connection_standard.h" -#elif defined BOARD_FULLSPEC -#include "buses/connection_type/connection_fullspec.h" -#elif defined BOARD_AIBOM -#include "buses/connection_type/connection_aibom.h" -#elif defined BOARD_GAMERNIUM -#include "buses/connection_type/connection_gamernium.h" -#else -#error Invalid connection type or none specified -#endif - -using namespace std; - -//--------------------------------------------------------------------------- -// -// SIGNAL_CONTROL_MODE: Signal control mode selection -// You can customize the signal control logic from Version 1.22 -// -// 0: SCSI logical specification -// Conversion board using 74LS641-1 etc. directly connected or published on HP -// True : 0V -// False : Open collector output (disconnect from bus) -// -// 1: Negative logic specification (when using conversion board for negative logic -> SCSI logic) -// There is no conversion board with this specification at this time -// True : 0V -> (CONVERT) -> 0V -// False : 3.3V -> (CONVERT) -> Open collector output -// -// 2: Positive logic specification (when using the conversion board for positive logic -> SCSI logic) -// PiSCSI Adapter Rev.C @132sync etc. -// -// True : 3.3V -> (CONVERT) -> 0V -// False : 0V -> (CONVERT) -> Open collector output -// -//--------------------------------------------------------------------------- - -//--------------------------------------------------------------------------- -// -// Control signal pin assignment setting -// GPIO pin mapping table for control signals. -// -// Control signal: -// PIN_ACT -// Signal that indicates the status of processing SCSI command. -// PIN_ENB -// Signal that indicates the valid signal from start to finish. -// PIN_TAD -// Signal that indicates the input/output direction of the target signal (BSY,IO,CD,MSG,REG). -// PIN_IND -// Signal that indicates the input/output direction of the initiator signal (SEL, ATN, RST, ACK). -// PIN_DTD -// Signal that indicates the input/output direction of the data lines (DT0...DT7,DP). -// -//--------------------------------------------------------------------------- - -//--------------------------------------------------------------------------- -// -// Control signal output logic -// 0V:FALSE 3.3V:TRUE -// -// ACT_ON -// PIN_ACT signal -// ENB_ON -// PIN_ENB signal -// TAD_IN -// PIN_TAD This is the logic when inputting. -// IND_IN -// PIN_ENB This is the logic when inputting. -// DTD_IN -// PIN_ENB This is the logic when inputting. -// -//--------------------------------------------------------------------------- - -//--------------------------------------------------------------------------- -// -// SCSI signal pin assignment setting -// GPIO pin mapping table for SCSI signals. -// PIN_DT0~PIN_SEL -// -//--------------------------------------------------------------------------- - -// Constant declarations (GPIO) -constexpr static int GPIO_INPUT = 0; -constexpr static int GPIO_OUTPUT = 1; -constexpr static int GPIO_IRQ_IN = 3; -constexpr static int GPIO_PULLNONE = 0; -constexpr static int GPIO_PULLDOWN = 1; -constexpr static int GPIO_PULLUP = 2; - -// Constant declarations (Control signals) -#define ACT_OFF !ACT_ON -#define ENB_OFF !ENB_ON -#define TAD_OUT !TAD_IN -#define IND_OUT !IND_IN -#define DTD_OUT !DTD_IN - -// Constant declarations (SCSI) -constexpr static int IN = GPIO_INPUT; -constexpr static int OUT = GPIO_OUTPUT; -constexpr static int ON = 1; -constexpr static int OFF = 0; - -class GpioBus : public Bus -{ - -public: - - bool Init(bool = true) override; - - int CommandHandShake(vector&) override; - int MsgInHandShake() override; - int ReceiveHandShake(uint8_t*, int) override; - int SendHandShake(uint8_t*, int, int = SEND_NO_DELAY) override; - - bool WaitSignal(int, bool); - -protected: - - inline bool IsTarget() const - { - return target_mode; - } - - virtual void EnableIRQ() = 0; - virtual void DisableIRQ() = 0; - - // Set GPIO output signal - virtual void PinSetSignal(int, bool) = 0; - - virtual void WaitBusSettle() const = 0; - -private: - - bool target_mode = true; - - // The DaynaPort SCSI Link do a short delay in the middle of transfering - // a packet. This is the number of ns that will be delayed between the - // header and the actual data. - constexpr static int SCSI_DELAY_SEND_DATA_DAYNAPORT_NS = 100'000; -}; diff --git a/cpp/buses/in_process_bus.cpp b/cpp/buses/in_process_bus.cpp index cb4bc7ac..e4a2c965 100644 --- a/cpp/buses/in_process_bus.cpp +++ b/cpp/buses/in_process_bus.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2023-2024 Uwe Seimet // @@ -13,7 +13,7 @@ using namespace spdlog; bool InProcessBus::Init(bool target) { - if (!GpioBus::Init(target)) { + if (!Bus::Init(target)) { return false; } @@ -98,5 +98,5 @@ void DelegatingInProcessBus::SetSignal(int pin, bool state) string DelegatingInProcessBus::GetSignalName(int pin) const { const auto &it = SIGNALS.find(pin); - return it != SIGNALS.end() ? it->second : "????"; + return it != SIGNALS.end() ? it->second : "???"; } diff --git a/cpp/buses/in_process_bus.h b/cpp/buses/in_process_bus.h index a4643fb7..8c7bfde9 100644 --- a/cpp/buses/in_process_bus.h +++ b/cpp/buses/in_process_bus.h @@ -1,20 +1,18 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // -// Copyright (C) 2023 Uwe Seimet +// Copyright (C) 2023-2024 Uwe Seimet // //--------------------------------------------------------------------------- #pragma once -#include -#include #include #include -#include "buses/gpio_bus.h" +#include "bus.h" -class InProcessBus : public GpioBus +class InProcessBus : public Bus { public: @@ -29,7 +27,6 @@ class InProcessBus : public GpioBus bool Init(bool) override; void CleanUp() override; - void Reset() override; uint32_t Acquire() override @@ -37,65 +34,16 @@ class InProcessBus : public GpioBus return dat; } - bool GetBSY() const override - { - return GetSignal(PIN_BSY); - } void SetBSY(bool state) override { SetSignal(PIN_BSY, state); } - bool GetSEL() const override - { - return GetSignal(PIN_SEL); - } + void SetSEL(bool state) override { SetSignal(PIN_SEL, state); } - bool GetATN() const override - { - return GetSignal(PIN_ATN); - } - void SetATN(bool state) override - { - SetSignal(PIN_ATN, state); - } - bool GetACK() const override - { - return GetSignal(PIN_ACK); - } - void SetACK(bool state) override - { - SetSignal(PIN_ACK, state); - } - bool GetRST() const override - { - return GetSignal(PIN_RST); - } - void SetRST(bool state) override - { - SetSignal(PIN_RST, state); - } - ; - bool GetMSG() const override - { - return GetSignal(PIN_MSG); - } - ; - void SetMSG(bool state) override - { - SetSignal(PIN_MSG, state); - } - ; - bool GetCD() const override - { - return GetSignal(PIN_CD); - } - void SetCD(bool state) override - { - SetSignal(PIN_CD, state); - } + bool GetIO() override { return GetSignal(PIN_IO); @@ -104,24 +52,6 @@ class InProcessBus : public GpioBus { SetSignal(PIN_IO, state); } - bool GetREQ() const override - { - return GetSignal(PIN_REQ); - } - void SetREQ(bool state) override - { - SetSignal(PIN_REQ, state); - } - - bool WaitREQ(bool state) override - { - return WaitSignal(PIN_REQ, state); - } - - bool WaitACK(bool state) override - { - return WaitSignal(PIN_ACK, state); - } uint8_t GetDAT() override { @@ -132,7 +62,7 @@ class InProcessBus : public GpioBus dat = d; } - bool GetSignal(int pin) const override; + bool GetSignal(int) const override; void SetSignal(int, bool) override; bool WaitForSelection() override; @@ -157,27 +87,6 @@ class InProcessBus : public GpioBus // Nothing to do } } - void SetControl(int, bool) override - { - assert(false); - } - void SetMode(int, int) override - { - assert(false); - } - void PinConfig(int, int) override - { - assert(false); - } - void PullConfig(int, int) override - { - assert(false); - } - void PinSetSignal(int, bool) override - { - assert(false); - } - static inline atomic_bool target_enabled; mutex write_locker; @@ -209,14 +118,9 @@ class DelegatingInProcessBus : public InProcessBus return bus.Acquire(); } - bool WaitREQ(bool state) override - { - return bus.WaitSignal(PIN_REQ, state); - } - - bool WaitACK(bool state) override + bool WaitSignal(int pin, bool state) override { - return bus.WaitSignal(PIN_ACK, state); + return bus.WaitSignal(pin, state); } uint8_t GetDAT() override diff --git a/cpp/buses/pin_control.h b/cpp/buses/pin_control.h deleted file mode 100644 index 0e4c3b9a..00000000 --- a/cpp/buses/pin_control.h +++ /dev/null @@ -1,64 +0,0 @@ -//--------------------------------------------------------------------------- -// -// SCSI target emulator and SCSI tools for the Raspberry Pi -// -// Copyright (C) 2022 akuker -// Copyright (C) 2023-2024 Uwe Seimet -// -// Virtual base class with methods to control the GPIO pins -// -//--------------------------------------------------------------------------- - -#include - -#pragma once - -class PinControl -{ - -public: - - PinControl() = default; - virtual ~PinControl() = default; - - virtual bool GetBSY() const = 0; - virtual void SetBSY(bool) = 0; - - virtual bool GetSEL() const = 0; - virtual void SetSEL(bool) = 0; - - virtual bool GetATN() const = 0; - virtual void SetATN(bool) = 0; - - virtual bool GetACK() const = 0; - virtual void SetACK(bool) = 0; - - virtual bool GetRST() const = 0; - virtual void SetRST(bool) = 0; - - virtual bool GetMSG() const = 0; - virtual void SetMSG(bool) = 0; - - virtual bool GetCD() const = 0; - virtual void SetCD(bool) = 0; - - virtual bool GetIO() = 0; - virtual void SetIO(bool) = 0; - - virtual bool GetREQ() const = 0; - virtual void SetREQ(bool) = 0; - - virtual uint8_t GetDAT() = 0; - virtual void SetDAT(uint8_t) = 0; - - // GPIO pin direction setting - virtual void PinConfig(int, int) = 0; - - // GPIO pin pull up/down resistor setting - virtual void PullConfig(int, int) = 0; - - virtual void SetControl(int, bool) = 0; - - // Sets signal direction (in/out) depending on initiator/target mode - virtual void SetMode(int, int) = 0; -}; diff --git a/cpp/buses/rpi_bus.cpp b/cpp/buses/rpi_bus.cpp index d0feac48..d5231924 100644 --- a/cpp/buses/rpi_bus.cpp +++ b/cpp/buses/rpi_bus.cpp @@ -1,15 +1,12 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2016-2020 GIMONS // Copyright (C) 2023-2024 Uwe Seimet // //--------------------------------------------------------------------------- -#include -#include -#include #include #include #include @@ -20,30 +17,40 @@ using namespace spdlog; bool RpiBus::Init(bool target) { - GpioBus::Init(target); + Bus::Init(target); - // Determine the Raspberry Pi type from the base address - const auto base_addr = GetPeripheralAddress(); - switch (base_addr) { - case 0xfe000000: - pi_type = PiType::pi_4; + int fd = open("/dev/mem", O_RDWR | O_SYNC); + if (fd == -1) { + critical("Root permissions are required"); + return false; + } + + off_t base_addr = 0; + uint32_t gpio_offset = GPIO_OFFSET; + uint32_t pads_offset = PADS_OFFSET; + switch (pi_type) { + case RpiBus::PiType::pi_1: + base_addr = 0x20000000; break; - case 0x3f000000: - pi_type = PiType::pi_2_3; + case RpiBus::PiType::pi_2: + case RpiBus::PiType::pi_3: + base_addr = 0x3f000000; break; - default: - pi_type = PiType::pi_1; + case RpiBus::PiType::pi_4: + base_addr = 0xfe000000; break; - } - trace("Detected Raspberry Pi type {}", static_cast(pi_type)); + case RpiBus::PiType::pi_5: + base_addr = 0x1f00000000; + gpio_offset = GPIO_OFFSET_PI5; + pads_offset = PADS_OFFSET_PI5; + break; - int fd = open("/dev/mem", O_RDWR | O_SYNC); - if (fd == -1) { - critical("Root permissions are required"); - return false; + default: + assert(false); + break; } // Map peripheral region memory @@ -70,7 +77,7 @@ bool RpiBus::Init(bool target) const array maxclock = { 32, 0, 0x00030004, 8, 0, 4, 0, 0 }; if (const int vcio_fd = open("/dev/vcio", O_RDONLY); vcio_fd != -1) { ioctl(vcio_fd, _IOWR(100, 0, char*), maxclock.data()); - corefreq = maxclock[6] / 1000000; + timer_core_freq = maxclock[6] / 1'000'000; close(vcio_fd); } else { @@ -78,32 +85,34 @@ bool RpiBus::Init(bool target) return false; } - armtaddr = map + ARMT_OFFSET / sizeof(uint32_t); + armt_addr = map + ARMT_OFFSET / sizeof(uint32_t); // Change the ARM timer to free run mode - armtaddr[ARMT_CTRL] = 0x00000282; + armt_addr[ARMT_CTRL] = 0x00000282; // GPIO - gpio = map + GPIO_OFFSET / sizeof(uint32_t); + gpio = map + gpio_offset / sizeof(uint32_t); level = &gpio[GPIO_LEV_0]; // PADS - pads = map + PADS_OFFSET / sizeof(uint32_t); + pads = map + pads_offset / sizeof(uint32_t); - // Interrupt controller - irpctl = map + IRPT_OFFSET / sizeof(uint32_t); + // Interrupt controller (Pi 1) + irp_ctl = map + IRPT_OFFSET / sizeof(uint32_t); - // Quad-A7 control - qa7regs = map + QA7_OFFSET / sizeof(uint32_t); + // Quad-A7 control (Pi 2/3) + qa7_regs = map + QA7_OFFSET / sizeof(uint32_t); + // Map GIC interrupt priority mask register if (pi_type == PiType::pi_4) { - map = static_cast(mmap(nullptr, 8192, PROT_READ | PROT_WRITE, MAP_SHARED, fd, ARM_GICD_BASE)); - if (map == MAP_FAILED) { - critical("Can't map GIC memory: {}", strerror(errno)); + gicc_mpr = static_cast(mmap(nullptr, 8, PROT_READ | PROT_WRITE, MAP_SHARED, fd, PI4_ARM_GICC_CTLR)); + if (gicc_mpr == MAP_FAILED) { + critical("Can't map GIC: {}", strerror(errno)); close(fd); return false; } - gicc = map + (ARM_GICC_BASE - ARM_GICD_BASE) / sizeof(uint32_t); + // MPR has offset 1 + ++gicc_mpr; } close(fd); @@ -113,20 +122,11 @@ bool RpiBus::Init(bool target) // Set pull up/pull down #if SIGNAL_CONTROL_MODE == 0 - int pullmode = GPIO_PULLNONE; -#elif SIGNAL_CONTROL_MODE == 1 - int pullmode = GPIO_PULLUP; + InitializeSignals(GPIO_PULLNONE); #else - int pullmode = GPIO_PULLDOWN; + InitializeSignals(GPIO_PULLDOWN); #endif - // Initialize all signals - for (const int signal : SignalTable) { - PinSetSignal(signal, false); - PinConfig(signal, GPIO_INPUT); - PullConfig(signal, pullmode); - } - // Set control signals PinSetSignal(PIN_ACT, false); PinSetSignal(PIN_TAD, false); @@ -137,16 +137,13 @@ bool RpiBus::Init(bool target) PinConfig(PIN_IND, GPIO_OUTPUT); PinConfig(PIN_DTD, GPIO_OUTPUT); - // Set the ENABLE signal - // This is used to show that the application is running PinSetSignal(PIN_ENB, ENB_OFF); PinConfig(PIN_ENB, GPIO_OUTPUT); - // GPIO Function Select (GPFSEL) registers backup + // GPIO Function Select (GPFSEL) registers copy gpfsel[0] = gpio[GPIO_FSEL_0]; gpfsel[1] = gpio[GPIO_FSEL_1]; gpfsel[2] = gpio[GPIO_FSEL_2]; - gpfsel[3] = gpio[GPIO_FSEL_3]; // Initialize SEL signal interrupt fd = open("/dev/gpiochip0", 0); @@ -160,7 +157,7 @@ bool RpiBus::Init(bool target) strcpy(selevreq.consumer_label, "SCSI2Pi"); // NOSONAR Using strcpy is safe selevreq.lineoffset = PIN_SEL; selevreq.handleflags = GPIOHANDLE_REQUEST_INPUT; -#if SIGNAL_CONTROL_MODE < 2 +#if SIGNAL_CONTROL_MODE == 0 selevreq.eventflags = GPIOEVENT_REQUEST_FALLING_EDGE; #else selevreq.eventflags = GPIOEVENT_REQUEST_RISING_EDGE; @@ -182,7 +179,7 @@ bool RpiBus::Init(bool target) CreateWorkTable(); - // Enable ENABLE in ordert to show the user that s2p is running + // Enable ENABLE in order to show the user that s2p is running SetControl(PIN_ENB, ENB_ON); return true; @@ -206,12 +203,7 @@ void RpiBus::CleanUp() PinConfig(PIN_IND, GPIO_INPUT); PinConfig(PIN_DTD, GPIO_INPUT); - // Initialize all signals - for (const int signal : SignalTable) { - PinSetSignal(signal, false); - PinConfig(signal, GPIO_INPUT); - PullConfig(signal, GPIO_PULLNONE); - } + InitializeSignals(GPIO_PULLNONE); // Set drive strength back to 8mA SetSignalDriveStrength(3); @@ -223,7 +215,7 @@ void RpiBus::Reset() SetControl(PIN_ACT, false); // Set all signals to off - for (const int signal : SignalTable) { + for (const int signal : SIGNAL_TABLE) { SetSignal(signal, false); } @@ -235,57 +227,35 @@ void RpiBus::Reset() SetMode(PIN_REQ, IN); SetMode(PIN_IO, IN); - if (IsTarget()) { - // Set the initiator signal to input - SetControl(PIN_IND, IND_IN); - SetMode(PIN_SEL, IN); - SetMode(PIN_ATN, IN); - SetMode(PIN_ACK, IN); - SetMode(PIN_RST, IN); - - // Set data bus signals to input - SetControl(PIN_DTD, DTD_IN); - SetMode(PIN_DT0, IN); - SetMode(PIN_DT1, IN); - SetMode(PIN_DT2, IN); - SetMode(PIN_DT3, IN); - SetMode(PIN_DT4, IN); - SetMode(PIN_DT5, IN); - SetMode(PIN_DT6, IN); - SetMode(PIN_DT7, IN); - SetMode(PIN_DP, IN); - } else { - // Set the initiator signal to output - SetControl(PIN_IND, IND_OUT); - SetMode(PIN_SEL, OUT); - SetMode(PIN_ATN, OUT); - SetMode(PIN_ACK, OUT); - SetMode(PIN_RST, OUT); - - // Set the data bus signals to output - SetControl(PIN_DTD, DTD_OUT); - SetMode(PIN_DT0, OUT); - SetMode(PIN_DT1, OUT); - SetMode(PIN_DT2, OUT); - SetMode(PIN_DT3, OUT); - SetMode(PIN_DT4, OUT); - SetMode(PIN_DT5, OUT); - SetMode(PIN_DT6, OUT); - SetMode(PIN_DT7, OUT); - SetMode(PIN_DP, OUT); + // Set the initiator signal direction + SetControl(PIN_IND, IsTarget() ? IND_IN : IND_OUT); + + // Set data bus signal directions + SetControl(PIN_DTD, IsTarget() ? DTD_IN : DTD_OUT); + + for (int pin : { PIN_SEL, PIN_ATN, PIN_ACK, PIN_RST, PIN_DT0, PIN_DT1, PIN_DT2, PIN_DT3, PIN_DT4, PIN_DT5, PIN_DT6, + PIN_DT7, PIN_DP }) { + SetMode(pin, IsTarget() ? IN : OUT); } // Initialize all signals signals = 0; } +void RpiBus::InitializeSignals(int pull_mode) +{ + for (const int signal : SIGNAL_TABLE) { + PinSetSignal(signal, false); + PinConfig(signal, GPIO_INPUT); + PullConfig(signal, pull_mode); + } +} + bool RpiBus::WaitForSelection() { #ifndef __linux__ return false; #else - errno = 0; - if (epoll_event epev; epoll_wait(epoll_fd, &epev, 1, -1) == -1) { if (errno != EINTR) { warn("epoll_wait failed: {}", strerror(errno)); @@ -306,130 +276,38 @@ bool RpiBus::WaitForSelection() return true; } -bool RpiBus::GetBSY() const -{ - return GetSignal(PIN_BSY); -} - void RpiBus::SetBSY(bool state) { SetSignal(PIN_BSY, state); - if (state) { - SetControl(PIN_ACT, true); - - SetControl(PIN_TAD, TAD_OUT); - - SetMode(PIN_BSY, OUT); - SetMode(PIN_MSG, OUT); - SetMode(PIN_CD, OUT); - SetMode(PIN_REQ, OUT); - SetMode(PIN_IO, OUT); - } else { - SetControl(PIN_ACT, false); + SetControl(PIN_ACT, state); + SetControl(PIN_TAD, state ? TAD_OUT : TAD_IN); - SetControl(PIN_TAD, TAD_IN); - - SetMode(PIN_BSY, IN); - SetMode(PIN_MSG, IN); - SetMode(PIN_CD, IN); - SetMode(PIN_REQ, IN); - SetMode(PIN_IO, IN); - } -} - -bool RpiBus::GetSEL() const -{ - return GetSignal(PIN_SEL); + SetMode(PIN_BSY, state ? OUT : IN); + SetMode(PIN_MSG, state ? OUT : IN); + SetMode(PIN_CD, state ? OUT : IN); + SetMode(PIN_REQ, state ? OUT : IN); + SetMode(PIN_IO, state ? OUT : IN); } void RpiBus::SetSEL(bool state) { - if (!IsTarget() && state) { - SetControl(PIN_ACT, true); - } + assert(!IsTarget()); + SetControl(PIN_ACT, state); SetSignal(PIN_SEL, state); } -bool RpiBus::GetATN() const -{ - return GetSignal(PIN_ATN); -} - -void RpiBus::SetATN(bool state) -{ - SetSignal(PIN_ATN, state); -} - -bool RpiBus::GetACK() const -{ - return GetSignal(PIN_ACK); -} - -void RpiBus::SetACK(bool state) -{ - SetSignal(PIN_ACK, state); -} - -bool RpiBus::GetRST() const -{ - return GetSignal(PIN_RST); -} - -void RpiBus::SetRST(bool state) -{ - SetSignal(PIN_RST, state); -} - -bool RpiBus::GetMSG() const -{ - return GetSignal(PIN_MSG); -} - -void RpiBus::SetMSG(bool state) -{ - SetSignal(PIN_MSG, state); -} - -bool RpiBus::GetCD() const -{ - return GetSignal(PIN_CD); -} - -void RpiBus::SetCD(bool state) -{ - SetSignal(PIN_CD, state); -} - bool RpiBus::GetIO() { - bool state = GetSignal(PIN_IO); + const bool state = GetSignal(PIN_IO); if (!IsTarget()) { // Change the data input/output direction by IO signal - if (state) { - SetControl(PIN_DTD, DTD_IN); - SetMode(PIN_DT0, IN); - SetMode(PIN_DT1, IN); - SetMode(PIN_DT2, IN); - SetMode(PIN_DT3, IN); - SetMode(PIN_DT4, IN); - SetMode(PIN_DT5, IN); - SetMode(PIN_DT6, IN); - SetMode(PIN_DT7, IN); - SetMode(PIN_DP, IN); - } else { - SetControl(PIN_DTD, DTD_OUT); - SetMode(PIN_DT0, OUT); - SetMode(PIN_DT1, OUT); - SetMode(PIN_DT2, OUT); - SetMode(PIN_DT3, OUT); - SetMode(PIN_DT4, OUT); - SetMode(PIN_DT5, OUT); - SetMode(PIN_DT6, OUT); - SetMode(PIN_DT7, OUT); - SetMode(PIN_DP, OUT); + SetControl(PIN_DTD, state ? DTD_IN : DTD_OUT); + + for (int pin : DATA_PINS) { + SetMode(pin, state ? IN : OUT); } } @@ -443,40 +321,11 @@ void RpiBus::SetIO(bool state) SetSignal(PIN_IO, state); // Change the data input/output direction by IO signal - if (state) { - SetControl(PIN_DTD, DTD_OUT); - SetDAT(0); - SetMode(PIN_DT0, OUT); - SetMode(PIN_DT1, OUT); - SetMode(PIN_DT2, OUT); - SetMode(PIN_DT3, OUT); - SetMode(PIN_DT4, OUT); - SetMode(PIN_DT5, OUT); - SetMode(PIN_DT6, OUT); - SetMode(PIN_DT7, OUT); - SetMode(PIN_DP, OUT); - } else { - SetControl(PIN_DTD, DTD_IN); - SetMode(PIN_DT0, IN); - SetMode(PIN_DT1, IN); - SetMode(PIN_DT2, IN); - SetMode(PIN_DT3, IN); - SetMode(PIN_DT4, IN); - SetMode(PIN_DT5, IN); - SetMode(PIN_DT6, IN); - SetMode(PIN_DT7, IN); - SetMode(PIN_DP, IN); - } -} - -bool RpiBus::GetREQ() const -{ - return GetSignal(PIN_REQ); -} + SetControl(PIN_DTD, state ? DTD_OUT : DTD_IN); -void RpiBus::SetREQ(bool state) -{ - SetSignal(PIN_REQ, state); + for (int pin : DATA_PINS) { + SetMode(pin, state ? OUT : IN); + } } inline uint8_t RpiBus::GetDAT() @@ -494,18 +343,22 @@ inline uint8_t RpiBus::GetDAT() #endif } -void RpiBus::SetDAT(uint8_t dat) +inline void RpiBus::SetDAT(uint8_t dat) { - // Write to port #if SIGNAL_CONTROL_MODE == 0 - uint32_t fsel; -#if !defined BOARD_STANDARD && !defined BOARD_FULLSPEC - fsel = gpfsel[0]; +#if defined BOARD_STANDARD || defined BOARD_FULLSPEC + uint32_t fsel = gpfsel[1]; + // Mask for the DT0-DT7 and DP pins + fsel &= 0b11111000000000000000000000000000; + fsel |= tblDatSet[1][dat]; + gpfsel[1] = fsel; + gpio[GPIO_FSEL_1] = fsel; +#else + uint32_t fsel = gpfsel[0]; fsel &= tblDatMsk[0][dat]; fsel |= tblDatSet[0][dat]; gpfsel[0] = fsel; gpio[GPIO_FSEL_0] = fsel; -#endif fsel = gpfsel[1]; fsel &= tblDatMsk[1][dat]; @@ -513,7 +366,6 @@ void RpiBus::SetDAT(uint8_t dat) gpfsel[1] = fsel; gpio[GPIO_FSEL_1] = fsel; -#if !defined BOARD_STANDARD && !defined BOARD_FULLSPEC fsel = gpfsel[2]; fsel &= tblDatMsk[2][dat]; fsel |= tblDatSet[2][dat]; @@ -526,18 +378,12 @@ void RpiBus::SetDAT(uint8_t dat) #endif } -const array RpiBus::SignalTable = { PIN_DT0, PIN_DT1, PIN_DT2, PIN_DT3, PIN_DT4, PIN_DT5, PIN_DT6, - PIN_DT7, PIN_DP, PIN_SEL, PIN_ATN, PIN_RST, PIN_ACK, PIN_BSY, - PIN_MSG, PIN_CD, PIN_IO, PIN_REQ }; - void RpiBus::CreateWorkTable(void) { - const array pintbl = { PIN_DT0, PIN_DT1, PIN_DT2, PIN_DT3, PIN_DT4, PIN_DT5, PIN_DT6, PIN_DT7, PIN_DP }; - array tblParity; // Create parity table - for (uint32_t i = 0; i < 0x100; i++) { + for (uint32_t i = 0; i < static_cast(tblParity.size()); i++) { uint32_t bits = i; uint32_t parity = 0; for (int j = 0; j < 8; j++) { @@ -549,15 +395,12 @@ void RpiBus::CreateWorkTable(void) } #if SIGNAL_CONTROL_MODE == 0 - // Mask and setting data generation + // Mask data defaults for (auto &tbl : tblDatMsk) { tbl.fill(-1); } - for (auto &tbl : tblDatSet) { - tbl.fill(0); - } - for (uint32_t i = 0; i < 0x100; i++) { + for (uint32_t i = 0; i < static_cast(tblParity.size()); i++) { // Bit string for inspection uint32_t bits = i; @@ -567,15 +410,15 @@ void RpiBus::CreateWorkTable(void) } // Bit check - for (int j = 0; j < 9; j++) { - // Index and shift amount calculation - int index = pintbl[j] / 10; - int shift = (pintbl[j] % 10) * 3; + for (const int pin : DATA_PINS) { + // Offset of the Function Select register for this pin (3 bits per pin) + const int index = pin / 10; + const int shift = (pin % 10) * 3; - // Mask data - tblDatMsk[index][i] &= ~(0x7 << shift); + // Mask data (GPIO pin is an output pin) + tblDatMsk[index][i] &= ~(7 << shift); - // Setting data + // Value (GPIO pin is set to 1) if (bits & 1) { tblDatSet[index][i] |= (1 << shift); } @@ -584,7 +427,7 @@ void RpiBus::CreateWorkTable(void) } } #else - for (uint32_t i = 0; i < 0x100; i++) { + for (uint32_t i = 0; i < static_cast(tblParity.size()); i++) { // Bit string for inspection uint32_t bits = i; @@ -593,19 +436,14 @@ void RpiBus::CreateWorkTable(void) bits |= (1 << 8); } -#if SIGNAL_CONTROL_MODE == 1 - // Negative logic is inverted - bits = ~bits; -#endif - // Create GPIO register information uint32_t gpclr = 0; uint32_t gpset = 0; - for (int j = 0; j < 9; j++) { + for (int j = 0; j < static_cast(DATA_PINS.size()); j++) { if (bits & 1) { - gpset |= (1 << pintbl[j]); + gpset |= (1 << pins[j]); } else { - gpclr |= (1 << pintbl[j]); + gpclr |= (1 << pins[j]); } bits >>= 1; } @@ -623,15 +461,16 @@ void RpiBus::SetControl(int pin, bool state) //--------------------------------------------------------------------------- // -// Input/output mode setting +// Input/output mode setting // // Set direction fo pin (IN / OUT) -// Used with: TAD, BSY, MSG, CD, REQ, O, SEL, IND, ATN, ACK, RST, DT* +// Used with: TAD, BSY, MSG, CD, REQ, I/O, SEL, IND, ATN, ACK, RST, DT* // //--------------------------------------------------------------------------- void RpiBus::SetMode(int pin, int mode) { #if SIGNAL_CONTROL_MODE == 0 + // Pins are implicitly set to OUT when applying the mask if (mode == OUT) { return; } @@ -639,8 +478,9 @@ void RpiBus::SetMode(int pin, int mode) const int index = pin / 10; const int shift = (pin % 10) * 3; + assert(index <= 2); uint32_t data = gpfsel[index]; - data &= ~(0x7 << shift); + data &= ~(7 << shift); if (mode == OUT) { data |= (1 << shift); } @@ -649,43 +489,38 @@ void RpiBus::SetMode(int pin, int mode) } // Get input signal value -bool RpiBus::GetSignal(int pin) const +inline bool RpiBus::GetSignal(int pin) const { return (signals >> pin) & 1; } //--------------------------------------------------------------------------- // -// Set output signal value +// Set output signal value // -// Sets the output value. Used with: -// PIN_ENB, ACT, TAD, IND, DTD, BSY, SignalTable +// Sets the output value. Used with: +// PIN_ENB, ACT, TAD, IND, DTD, BSY, SignalTable // //--------------------------------------------------------------------------- void RpiBus::SetSignal(int pin, bool state) { #if SIGNAL_CONTROL_MODE == 0 const int index = pin / 10; + assert(index <= 2); const int shift = (pin % 10) * 3; uint32_t data = gpfsel[index]; if (state) { data |= (1 << shift); } else { - data &= ~(0x7 << shift); + data &= ~(7 << shift); } gpio[index] = data; gpfsel[index] = data; -#elif SIGNAL_CONTROL_MODE == 1 - if (state) { - gpio[GPIO_CLR_0] = 0x1 << pin; - } else { - gpio[GPIO_SET_0] = 0x1 << pin; - } -#elif SIGNAL_CONTROL_MODE == 2 +#else if (state) { - gpio[GPIO_SET_0] = 0x1 << pin; + gpio[GPIO_SET_0] = 1 << pin; } else { - gpio[GPIO_CLR_0] = 0x1 << pin; + gpio[GPIO_CLR_0] = 1 << pin; } #endif } @@ -694,23 +529,27 @@ void RpiBus::DisableIRQ() { #ifdef __linux__ switch (pi_type) { - case PiType::pi_4: - // RPI4 disables interrupts via the GIC - giccpmr = gicc[GICC_PMR]; - gicc[GICC_PMR] = 0; + case PiType::pi_1: + // Stop system timer interrupt with interrupt controller + irpt_enb = irp_ctl[IRPT_ENB_IRQ_1]; + irp_ctl[IRPT_DIS_IRQ_1] = irpt_enb & 0xf; break; - - case PiType::pi_2_3: + case PiType::pi_2: + case PiType::pi_3: // RPI2,3 disable core timer IRQ - tintcore = sched_getcpu() + QA7_CORE0_TINTC; - tintctl = qa7regs[tintcore]; - qa7regs[tintcore] = 0; + tint_core = sched_getcpu() + QA7_CORE0_TINTC; + tint_ctl = qa7_regs[tint_core]; + qa7_regs[tint_core] = 0; + break; + + case PiType::pi_4: + // RPI4 disables interrupts via the GIC + gicc_pmr_saved = *gicc_mpr; + *gicc_mpr = 0; break; default: - // Stop system timer interrupt with interrupt controller - irptenb = irpctl[IRPT_ENB_IRQ_1]; - irpctl[IRPT_DIS_IRQ_1] = irptenb & 0xf; + // Currently do nothing break; } #endif @@ -720,19 +559,24 @@ void RpiBus::EnableIRQ() { #ifdef __linux__ switch (pi_type) { - case PiType::pi_4: - // RPI4 enables interrupts via the GIC - gicc[GICC_PMR] = giccpmr; + case PiType::pi_1: + // Restart the system timer interrupt with the interrupt controller + irp_ctl[IRPT_ENB_IRQ_1] = irpt_enb & 0xf; break; - case PiType::pi_2_3: + case PiType::pi_2: + case PiType::pi_3: // RPI2,3 re-enable core timer IRQ - qa7regs[tintcore] = tintctl; + qa7_regs[tint_core] = tint_ctl; + break; + + case PiType::pi_4: + // RPI4 enables interrupts via the GIC + *gicc_mpr = gicc_pmr_saved; break; default: - // Restart the system timer interrupt with the interrupt controller - irpctl[IRPT_ENB_IRQ_1] = irptenb & 0xf; + // Currently do nothing break; } #endif @@ -740,7 +584,7 @@ void RpiBus::EnableIRQ() //--------------------------------------------------------------------------- // -// Pin direction setting (input/output) +// Pin direction setting (input/output) // // Used in Init() for ACT, TAD, IND, DTD, ENB to set direction (GPIO_OUTPUT vs GPIO_INPUT) // Also used on SignalTable @@ -754,7 +598,7 @@ void RpiBus::PinConfig(int pin, int mode) } const int index = pin / 10; - uint32_t mask = ~(0x7 << ((pin % 10) * 3)); + uint32_t mask = ~(7 << ((pin % 10) * 3)); gpio[index] = (gpio[index] & mask) | ((mode & 0x7) << ((pin % 10) * 3)); } @@ -766,22 +610,19 @@ void RpiBus::PullConfig(int pin, int mode) return; } - if (pi_type == PiType::pi_4) { + if (pi_type >= PiType::pi_4) { uint32_t pull; switch (mode) { case GPIO_PULLNONE: pull = 0; break; - case GPIO_PULLUP: - pull = 1; - break; - case GPIO_PULLDOWN: pull = 2; break; default: + assert(false); return; } @@ -793,12 +634,12 @@ void RpiBus::PullConfig(int pin, int mode) gpio[GPIO_PUPPDN0 + (pin >> 4)] = bits; } else { // 2 us - const timespec ts = { .tv_sec = 0, .tv_nsec = 2'000 }; + constexpr timespec ts = { .tv_sec = 0, .tv_nsec = 2'000 }; pin &= 0x1f; gpio[GPIO_PUD] = mode & 0x3; nanosleep(&ts, nullptr); - gpio[GPIO_CLK_0] = 0x1 << pin; + gpio[GPIO_CLK_0] = 1 << pin; nanosleep(&ts, nullptr); gpio[GPIO_PUD] = 0; gpio[GPIO_CLK_0] = 0; @@ -825,7 +666,7 @@ inline uint32_t RpiBus::Acquire() { signals = *level; -#if SIGNAL_CONTROL_MODE < 2 +#if SIGNAL_CONTROL_MODE == 0 // Invert if negative logic (internal processing is unified to positive logic) signals = ~signals; #endif @@ -837,33 +678,10 @@ inline uint32_t RpiBus::Acquire() // nanosleep() does not provide the required resolution, which causes issues when reading data from the bus. void RpiBus::WaitBusSettle() const { - if (const uint32_t diff = corefreq * 400 / 1000; diff) { - const uint32_t start = armtaddr[ARMT_FREERUN]; - while (armtaddr[ARMT_FREERUN] - start < diff) { + if (const uint32_t diff = timer_core_freq * 400 / 1000; diff) { + const uint32_t start = armt_addr[ARMT_FREERUN]; + while (armt_addr[ARMT_FREERUN] - start < diff) { // Intentionally empty } } } - -uint32_t RpiBus::GetDtRanges(const string &filename, uint32_t offset) -{ - if (ifstream in(filename, ios::binary); in.good()) { - in.seekg(offset, ios::beg); - array buf; - in.read(buf.data(), buf.size()); - if (in.good()) { - return (int)buf[0] << 24 | (int)buf[1] << 16 | (int)buf[2] << 8 | (int)buf[3] << 0; - } - } - - return ~0; -} - -uint32_t RpiBus::GetPeripheralAddress() -{ - uint32_t address = GetDtRanges("/proc/device-tree/soc/ranges", 4); - if (!address) { - address = GetDtRanges("/proc/device-tree/soc/ranges", 8); - } - return address == (uint32_t)~0 ? 0x20000000 : address; -} diff --git a/cpp/buses/rpi_bus.h b/cpp/buses/rpi_bus.h index 71c948c6..3fc7c579 100644 --- a/cpp/buses/rpi_bus.h +++ b/cpp/buses/rpi_bus.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2016-2020 GIMONS // Copyright (C) 2023-2024 Uwe Seimet @@ -13,14 +13,26 @@ #include #include #endif -#include "gpio_bus.h" +#include "bus.h" -class RpiBus : public GpioBus +class RpiBus final : public Bus { public: - RpiBus() = default; + enum class PiType + { + unknown = 0, + pi_1 = 1, + pi_2 = 2, + pi_3 = 3, + pi_4 = 4, + pi_5 = 5 + }; + + explicit RpiBus(PiType t) : pi_type(t) + { + } ~RpiBus() override = default; bool Init(bool = true) override; @@ -33,60 +45,28 @@ class RpiBus : public GpioBus // Bus signal acquisition uint32_t Acquire() override; - bool GetBSY() const override; void SetBSY(bool) override; - bool GetSEL() const override; void SetSEL(bool) override; - bool GetATN() const override; - void SetATN(bool) override; - - bool GetACK() const override; - void SetACK(bool) override; - - bool GetRST() const override; - void SetRST(bool) override; - - bool GetMSG() const override; - void SetMSG(bool) override; - - bool GetCD() const override; - void SetCD(bool) override; - bool GetIO() override; - void SetIO(bool ast) override; - - bool GetREQ() const override; - void SetREQ(bool ast) override; + void SetIO(bool) override; uint8_t GetDAT() override; void SetDAT(uint8_t) override; - bool WaitREQ(bool ast) override - { - return WaitSignal(PIN_REQ, ast); - } - inline bool WaitACK(bool ast) override - { - return WaitSignal(PIN_ACK, ast); - } void WaitBusSettle() const override; private: - enum class PiType - { - unknown = 0, - pi_1 = 1, - pi_2_3 = 2, - pi_4 = 4 - }; + void InitializeSignals(int); void CreateWorkTable(); - void SetControl(int, bool) override; - void SetMode(int, int) override; + void SetControl(int, bool); + + // Sets signal direction (in/out) depending on initiator/target mode + void SetMode(int, int); bool GetSignal(int) const override; void SetSignal(int, bool) override; @@ -94,21 +74,22 @@ class RpiBus : public GpioBus void DisableIRQ() override; void EnableIRQ() override; - void PinConfig(int, int) override; - void PullConfig(int, int) override; - void PinSetSignal(int, bool) override; + //GPIO pin pull up/down resistor setting + void PullConfig(int, int); + + //GPIO pin direction setting + void PinConfig(int, int); + + void PinSetSignal(int, bool); // Set GPIO drive strength void SetSignalDriveStrength(uint32_t); - static uint32_t GetPeripheralAddress(); - static uint32_t GetDtRanges(const string&, uint32_t); - - PiType pi_type = PiType::unknown; + PiType pi_type; - inline static uint32_t corefreq = 0; + uint32_t timer_core_freq = 0; - volatile uint32_t *armtaddr = nullptr; + volatile uint32_t *armt_addr = nullptr; // GPIO register volatile uint32_t *gpio = nullptr; @@ -117,23 +98,23 @@ class RpiBus : public GpioBus volatile uint32_t *pads = nullptr; // Interrupt control register - volatile uint32_t *irpctl = nullptr; + volatile uint32_t *irp_ctl = nullptr; // QA7 register - volatile uint32_t *qa7regs = nullptr; + volatile uint32_t *qa7_regs = nullptr; #ifdef __linux__ // Interrupt enabled state - uint32_t irptenb; + uint32_t irpt_enb; // Interupt control target CPU - int tintcore; + int tint_core; // Interupt control - uint32_t tintctl; + uint32_t tint_ctl; - // GICC priority setting - uint32_t giccpmr; + // GIC priority setting + uint32_t gicc_pmr_saved; // SEL signal event request struct gpioevent_request selevreq = { }; @@ -141,10 +122,11 @@ class RpiBus : public GpioBus #endif // GIC CPU interface register - volatile uint32_t *gicc = nullptr; + volatile uint32_t *gicc_mpr = nullptr; - // RAM copy of GPFSEL0-4 values (GPIO Function Select) - array gpfsel; + // RAM copy of GPFSEL0-2 values (GPIO Function Select) + // Reading the current data from the copy is faster than directly reading them from the ports + array gpfsel; // All bus signals uint32_t signals = 0; @@ -156,15 +138,19 @@ class RpiBus : public GpioBus // Data mask table array, 3> tblDatMsk; // Data setting table - array, 3> tblDatSet; - #else + array, 3> tblDatSet = { }; +#else // Data mask table array tblDatMsk = {}; // Table setting table array tblDatSet = {}; #endif - static const array SignalTable; + constexpr static array SIGNAL_TABLE = { PIN_DT0, PIN_DT1, PIN_DT2, PIN_DT3, PIN_DT4, PIN_DT5, PIN_DT6, + PIN_DT7, PIN_DP, PIN_SEL, PIN_ATN, PIN_RST, PIN_ACK, PIN_BSY, PIN_MSG, PIN_CD, PIN_IO, PIN_REQ }; + + constexpr static array DATA_PINS = { PIN_DT0, PIN_DT1, PIN_DT2, PIN_DT3, PIN_DT4, PIN_DT5, PIN_DT6, PIN_DT7, + PIN_DP }; constexpr static int ARMT_CTRL = 2; constexpr static int ARMT_FREERUN = 8; @@ -174,7 +160,6 @@ class RpiBus : public GpioBus constexpr static int GPIO_FSEL_0 = 0; constexpr static int GPIO_FSEL_1 = 1; constexpr static int GPIO_FSEL_2 = 2; - constexpr static int GPIO_FSEL_3 = 3; constexpr static int GPIO_SET_0 = 7; constexpr static int GPIO_CLR_0 = 10; constexpr static int GPIO_LEV_0 = 13; @@ -188,10 +173,11 @@ class RpiBus : public GpioBus constexpr static uint32_t IRPT_OFFSET = 0x0000B200; constexpr static uint32_t PADS_OFFSET = 0x00100000; + constexpr static uint32_t PADS_OFFSET_PI5 = 0x000f0000; constexpr static uint32_t GPIO_OFFSET = 0x00200000; + constexpr static uint32_t GPIO_OFFSET_PI5 = 0x000d0000; + constexpr static uint32_t RIO_OFFSET_PI5 = 0x000e0000; constexpr static uint32_t QA7_OFFSET = 0x01000000; - constexpr static uint32_t ARM_GICD_BASE = 0xFF841000; - constexpr static uint32_t ARM_GICC_BASE = 0xFF842000; - constexpr static int GICC_PMR = 0x001; + constexpr static uint32_t PI4_ARM_GICC_CTLR = 0xFF842000; }; diff --git a/cpp/command/command_dispatcher.cpp b/cpp/command/command_dispatcher.cpp index 6cd81db1..6d3b715d 100644 --- a/cpp/command/command_dispatcher.cpp +++ b/cpp/command/command_dispatcher.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -260,26 +260,24 @@ bool CommandDispatcher::SetLogLevel(const string &log_level) const level::level_enum l = level::from_str(level); // Compensate for spdlog using 'off' for unknown levels if (to_string_view(l) != level) { - warn("Invalid log level '" + level + "'"); + warn("Invalid log level '{}'", level); return false; } set_level(l); DeviceLogger::SetLogIdAndLun(id, lun); - string msg; if (id != -1) { if (lun == -1) { - msg = fmt::format("Set log level for device {0} to '{1}'", id, level); + info("Set log level for device {0} to '{1}'", id, level); } else { - msg = fmt::format("Set log level for device {0}:{1} to '{2}'", id, lun, level); + info("Set log level for device {0}:{1} to '{2}'", id, lun, level); } } else { - msg = fmt::format("Set log level to '{}'", level); + info("Set log level to '{}'", level); } - info(msg); return true; } diff --git a/cpp/command/command_dispatcher.h b/cpp/command/command_dispatcher.h index db598338..6fa1f0da 100644 --- a/cpp/command/command_dispatcher.h +++ b/cpp/command/command_dispatcher.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // diff --git a/cpp/command/command_executor.cpp b/cpp/command/command_executor.cpp index 4a5638f5..5d7ec066 100644 --- a/cpp/command/command_executor.cpp +++ b/cpp/command/command_executor.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2024 Uwe Seimet // @@ -22,7 +22,13 @@ using namespace s2p_util; bool CommandExecutor::ProcessDeviceCmd(const CommandContext &context, const PbDeviceDefinition &pb_device, bool dryRun) { - info((dryRun ? "Validating: " : "Executing: ") + PrintCommand(context.GetCommand(), pb_device)); + const string &msg = PrintCommand(context.GetCommand(), pb_device); + if (dryRun) { + trace("Validating: " + msg); + } + else { + info("Executing: " + msg); + } const int id = pb_device.id(); const int lun = pb_device.unit(); @@ -45,12 +51,6 @@ bool CommandExecutor::ProcessDeviceCmd(const CommandContext &context, const PbDe } switch (operation) { - case START: - return Start(*device, dryRun); - - case STOP: - return Stop(*device, dryRun); - case ATTACH: return Attach(context, pb_device, dryRun); @@ -60,15 +60,20 @@ bool CommandExecutor::ProcessDeviceCmd(const CommandContext &context, const PbDe case INSERT: return Insert(context, pb_device, device, dryRun); + case START: + return dryRun ? true : Start(*device); + + case STOP: + return dryRun ? true : Stop(*device); + case EJECT: - return Eject(*device, dryRun); + return dryRun ? true : Eject(*device); case PROTECT: - return Protect(*device, dryRun); + return dryRun ? true : Protect(*device); case UNPROTECT: - return Unprotect(*device, dryRun); - break; + return dryRun ? true : Unprotect(*device); default: return context.ReturnLocalizedError(LocalizationKey::ERROR_OPERATION, to_string(operation)); @@ -118,8 +123,8 @@ bool CommandExecutor::ProcessCmd(const CommandContext &context) return false; } - if (const string error = EnsureLun0(command); !error.empty()) { - return context.ReturnErrorStatus(error); + if (!EnsureLun0(context, command)) { + return false; } if (ranges::find_if_not(command.devices(), [&](const auto &device) @@ -131,63 +136,53 @@ bool CommandExecutor::ProcessCmd(const CommandContext &context) return command.operation() == ATTACH || command.operation() == DETACH ? true : context.ReturnSuccessStatus(); } -bool CommandExecutor::Start(PrimaryDevice &device, bool dryRun) const +bool CommandExecutor::Start(PrimaryDevice &device) const { - if (!dryRun) { - info("Start requested for {}", GetIdentifier(device)); + info("Start requested for {}", GetIdentifier(device)); - if (!device.Start()) { - warn("Starting {} failed", GetIdentifier(device)); - } + if (!device.Start()) { + warn("Starting {} failed", GetIdentifier(device)); } return true; } -bool CommandExecutor::Stop(PrimaryDevice &device, bool dryRun) const +bool CommandExecutor::Stop(PrimaryDevice &device) const { - if (!dryRun) { - info("Stop requested for {}", GetIdentifier(device)); + info("Stop requested for {}", GetIdentifier(device)); - device.Stop(); + device.Stop(); - device.SetStatus(sense_key::no_sense, asc::no_additional_sense_information); - } + device.SetStatus(sense_key::no_sense, asc::no_additional_sense_information); return true; } -bool CommandExecutor::Eject(PrimaryDevice &device, bool dryRun) const +bool CommandExecutor::Eject(PrimaryDevice &device) const { - if (!dryRun) { - info("Eject requested for {}", GetIdentifier(device)); + info("Eject requested for {}", GetIdentifier(device)); - if (!device.Eject(true)) { - warn("Ejecting {} failed", GetIdentifier(device)); - } + if (!device.Eject(true)) { + warn("Ejecting {} failed", GetIdentifier(device)); } return true; } -bool CommandExecutor::Protect(PrimaryDevice &device, bool dryRun) const +bool CommandExecutor::Protect(PrimaryDevice &device) const { - if (!dryRun) { - info("Write protection requested for {}", GetIdentifier(device)); + info("Write protection requested for {}", GetIdentifier(device)); - device.SetProtected(true); - } + device.SetProtected(true); return true; } -bool CommandExecutor::Unprotect(PrimaryDevice &device, bool dryRun) const +bool CommandExecutor::Unprotect(PrimaryDevice &device) const { - if (!dryRun) { - info("Write unprotection requested for {}", GetIdentifier(device)); + info("Write unprotection requested for {}", GetIdentifier(device)); - device.SetProtected(false); - } + device.SetProtected(false); return true; } @@ -230,9 +225,6 @@ bool CommandExecutor::Attach(const CommandContext &context, const PbDeviceDefini return false; } - // If no filename was provided the medium is considered not inserted - device->SetRemoved(device->SupportsFile() ? filename.empty() : false); - if (!SetProductData(context, pb_device, *device)) { return false; } @@ -244,6 +236,9 @@ bool CommandExecutor::Attach(const CommandContext &context, const PbDeviceDefini #ifdef BUILD_DISK const auto storage_device = dynamic_pointer_cast(device); if (device->SupportsFile()) { + // If no filename was provided the medium is considered not inserted + device->SetRemoved(filename.empty()); + // The caching mode must be set before the file is accessed if (const auto disk = dynamic_pointer_cast(device)) { disk->SetCachingMode(caching_mode); @@ -272,7 +267,7 @@ bool CommandExecutor::Attach(const CommandContext &context, const PbDeviceDefini return true; } - param_map params = { pb_device.params().begin(), pb_device.params().end() }; + param_map params = { pb_device.params().cbegin(), pb_device.params().cend() }; if (!device->SupportsFile()) { // Legacy clients like scsictl might have sent both "file" and "interfaces" params.erase("file"); @@ -295,15 +290,7 @@ bool CommandExecutor::Attach(const CommandContext &context, const PbDeviceDefini SetUpDeviceProperties(context, device); - string msg = "Attached "; - if (device->IsReadOnly()) { - msg += "read-only "; - } - else if (device->IsProtectable() && device->IsProtected()) { - msg += "protected "; - } - msg += GetIdentifier(*device); - info(msg); + DisplayDeviceInfo(*device); return true; } @@ -348,9 +335,12 @@ bool CommandExecutor::Insert(const CommandContext &context, const PbDeviceDefini return false; } - storage_device->SetProtected(pb_device.protected_()); - storage_device->ReserveFile(); + if (!storage_device->ReserveFile()) { + return false; + } + storage_device->SetMediumChanged(true); + storage_device->SetProtected(pb_device.protected_()); #endif return true; @@ -370,8 +360,8 @@ bool CommandExecutor::Detach(const CommandContext &context, PrimaryDevice &devic } if (!dryRun) { - // Remember some device data before the device data become invalid on removal - const string identifier = GetIdentifier(device); + // Remember some device data before they become invalid on removal + const string &identifier = GetIdentifier(device); const int id = device.GetId(); const int lun = device.GetLun(); @@ -433,6 +423,19 @@ void CommandExecutor::SetUpDeviceProperties(const CommandContext &context, share } } +void CommandExecutor::DisplayDeviceInfo(const PrimaryDevice &device) +{ + string msg = "Attached "; + if (device.IsReadOnly()) { + msg += "read-only "; + } + else if (device.IsProtectable() && device.IsProtected()) { + msg += "protected "; + } + msg += GetIdentifier(device); + info(msg); +} + string CommandExecutor::SetReservedIds(string_view ids) { set ids_to_reserve; @@ -451,7 +454,7 @@ string CommandExecutor::SetReservedIds(string_view ids) ids_to_reserve.insert(res_id); } - reserved_ids = { ids_to_reserve.begin(), ids_to_reserve.end() }; + reserved_ids = { ids_to_reserve.cbegin(), ids_to_reserve.cend() }; if (!ids_to_reserve.empty()) { info("Reserved ID(s) set to {}", Join(ids_to_reserve)); @@ -477,19 +480,19 @@ bool CommandExecutor::ValidateImageFile(const CommandContext &context, StorageDe return false; } - storage_device.SetFilename(filename); + string effective_filename = filename; if (!StorageDevice::FileExists(filename)) { // If the file does not exist search for it in the default image folder - const string effective_filename = context.GetDefaultFolder() + "/" + filename; + effective_filename = context.GetDefaultFolder() + "/" + filename; if (!CheckForReservedFile(context, effective_filename)) { return false; } - - storage_device.SetFilename(effective_filename); } + storage_device.SetFilename(effective_filename); + try { storage_device.Open(); } @@ -517,9 +520,9 @@ bool CommandExecutor::CheckForReservedFile(const CommandContext &context, const } #pragma GCC diagnostic pop -string CommandExecutor::PrintCommand(const PbCommand &command, const PbDeviceDefinition &pb_device) const +string CommandExecutor::PrintCommand(const PbCommand &command, const PbDeviceDefinition &pb_device) { - const map> params = { command.params().begin(), command.params().end() }; + const map> params = { command.params().cbegin(), command.params().cend() }; ostringstream s; s << "operation=" << PbOperation_Name(command.operation()); @@ -576,9 +579,9 @@ string CommandExecutor::PrintCommand(const PbCommand &command, const PbDeviceDef return s.str(); } -string CommandExecutor::EnsureLun0(const PbCommand &command) const +bool CommandExecutor::EnsureLun0(const CommandContext &context, const PbCommand &command) const { - // Mapping of available LUNs (bit vector) to devices + // Mapping of available LUNs (bit vector) to device IDs unordered_map luns; // Collect LUN bit vectors of new devices @@ -592,7 +595,9 @@ string CommandExecutor::EnsureLun0(const PbCommand &command) const } const auto &it = ranges::find_if_not(luns, [](const auto &l) {return l.second & 0x01;}); - return it == luns.end() ? "" : "LUN 0 is missing for device ID " + to_string((*it).first); + return + it == luns.end() ? + true : context.ReturnLocalizedError(LocalizationKey::ERROR_MISSING_LUN0, to_string((*it).first)); } bool CommandExecutor::VerifyExistingIdAndLun(const CommandContext &context, int id, int lun) const diff --git a/cpp/command/command_executor.h b/cpp/command/command_executor.h index fe314b23..ca42ce80 100644 --- a/cpp/command/command_executor.h +++ b/cpp/command/command_executor.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2024 Uwe Seimet // @@ -34,28 +34,23 @@ class CommandExecutor bool ProcessDeviceCmd(const CommandContext&, const PbDeviceDefinition&, bool); bool ProcessCmd(const CommandContext&); - bool Start(PrimaryDevice&, bool) const; - bool Stop(PrimaryDevice&, bool) const; - bool Eject(PrimaryDevice&, bool) const; - bool Protect(PrimaryDevice&, bool) const; - bool Unprotect(PrimaryDevice&, bool) const; + bool Start(PrimaryDevice&) const; + bool Stop(PrimaryDevice&) const; + bool Eject(PrimaryDevice&) const; + bool Protect(PrimaryDevice&) const; + bool Unprotect(PrimaryDevice&) const; bool Attach(const CommandContext&, const PbDeviceDefinition&, bool); bool Insert(const CommandContext&, const PbDeviceDefinition&, const shared_ptr&, bool) const; bool Detach(const CommandContext&, PrimaryDevice&, bool) const; void DetachAll() const; string SetReservedIds(string_view); bool ValidateImageFile(const CommandContext&, StorageDevice&, const string&) const; - string PrintCommand(const PbCommand&, const PbDeviceDefinition&) const; - string EnsureLun0(const PbCommand&) const; + bool EnsureLun0(const CommandContext&, const PbCommand&) const; bool VerifyExistingIdAndLun(const CommandContext&, int, int) const; shared_ptr CreateDevice(const CommandContext&, const PbDeviceType, int, const string&) const; bool SetScsiLevel(const CommandContext&, shared_ptr, int) const; bool SetSectorSize(const CommandContext&, shared_ptr, int) const; - static bool ValidateOperationAgainstDevice(const CommandContext&, const PrimaryDevice&, PbOperation); - static bool ValidateIdAndLun(const CommandContext&, int, int); - static bool SetProductData(const CommandContext&, const PbDeviceDefinition&, PrimaryDevice&); - mutex& GetExecutionLocker() { return execution_locker; @@ -66,6 +61,11 @@ class CommandExecutor return controller_factory->GetAllDevices(); } + static bool ValidateOperationAgainstDevice(const CommandContext&, const PrimaryDevice&, PbOperation); + static bool ValidateIdAndLun(const CommandContext&, int, int); + static bool SetProductData(const CommandContext&, const PbDeviceDefinition&, PrimaryDevice&); + static string PrintCommand(const PbCommand&, const PbDeviceDefinition&); + private: static string GetIdentifier(const Device &device) @@ -73,7 +73,7 @@ class CommandExecutor return device.GetTypeString() + " " + to_string(device.GetId()) + ":" + to_string(device.GetLun()); } - + static void DisplayDeviceInfo(const PrimaryDevice&); static bool CheckForReservedFile(const CommandContext&, const string&); static void SetUpDeviceProperties(const CommandContext&, shared_ptr); diff --git a/cpp/command/command_response.cpp b/cpp/command/command_response.cpp index 5efc3a3a..71c9c692 100644 --- a/cpp/command/command_response.cpp +++ b/cpp/command/command_response.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2024 Uwe Seimet // @@ -42,7 +42,7 @@ void CommandResponse::GetDeviceProperties(shared_ptr device, PbDe } #ifdef BUILD_DISK - if (shared_ptr disk = dynamic_pointer_cast(device); disk && disk->IsSectorSizeConfigurable()) { + if (const auto disk = dynamic_pointer_cast(device); disk && disk->IsSectorSizeConfigurable()) { for (const auto §or_size : disk->GetSupportedSectorSizes()) { properties.add_block_sizes(sector_size); } @@ -50,25 +50,18 @@ void CommandResponse::GetDeviceProperties(shared_ptr device, PbDe #endif } -void CommandResponse::GetDeviceTypeProperties(PbDeviceTypesInfo &device_types_info, PbDeviceType type) const -{ - auto type_properties = device_types_info.add_properties(); - type_properties->set_type(type); - const auto device = DeviceFactory::Instance().CreateDevice(type, 0, ""); - GetDeviceProperties(device, *type_properties->mutable_properties()); -} - void CommandResponse::GetDeviceTypesInfo(PbDeviceTypesInfo &device_types_info) const { int ordinal = 1; while (PbDeviceType_IsValid(ordinal)) { - PbDeviceType type = UNDEFINED; - PbDeviceType_Parse(PbDeviceType_Name((PbDeviceType)ordinal), &type); - // Only report device types actually supported by the factory - if (DeviceFactory::Instance().CreateDevice(type, 0, "")) { - GetDeviceTypeProperties(device_types_info, type); + // Only report device types supported by the factory + if (const auto device = DeviceFactory::Instance().CreateDevice(static_cast(ordinal), 0, ""); device) { + auto type_properties = device_types_info.add_properties(); + type_properties->set_type(device->GetType()); + GetDeviceProperties(device, *type_properties->mutable_properties()); } - ordinal++; + + ++ordinal; } } @@ -104,11 +97,8 @@ void CommandResponse::GetDevice(shared_ptr device, PbDevice &pb_d pb_device.set_block_size(disk->IsRemoved() ? 0 : disk->GetSectorSizeInBytes()); pb_device.set_block_count(disk->IsRemoved() ? 0 : disk->GetBlockCount()); pb_device.set_caching_mode(disk->GetCachingMode()); - } - const auto storage_device = dynamic_pointer_cast(device); - if (storage_device) { - GetImageFile(*pb_device.mutable_file(), default_folder, device->IsReady() ? storage_device->GetFilename() : ""); + GetImageFile(*pb_device.mutable_file(), default_folder, disk->IsReady() ? disk->GetFilename() : ""); } #endif } diff --git a/cpp/command/command_response.h b/cpp/command/command_response.h index c737e119..67d2ab36 100644 --- a/cpp/command/command_response.h +++ b/cpp/command/command_response.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2024 Uwe Seimet // @@ -41,7 +41,6 @@ class CommandResponse void GetDeviceProperties(shared_ptr, PbDeviceProperties&) const; void GetDevice(shared_ptr, PbDevice&, const string&) const; - void GetDeviceTypeProperties(PbDeviceTypesInfo&, PbDeviceType) const; void GetAvailableImages(PbImageFilesInfo&, const string&, const string&, const string&, int) const; void GetAvailableImages(PbServerInfo&, const string&, const string&, const string&, int) const; PbOperationMetaData* CreateOperation(PbOperationInfo&, const PbOperation&, const string&) const; diff --git a/cpp/command/image_support.cpp b/cpp/command/image_support.cpp index bc69d605..eaa9e0eb 100644 --- a/cpp/command/image_support.cpp +++ b/cpp/command/image_support.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2024 Uwe Seimet // diff --git a/cpp/command/image_support.h b/cpp/command/image_support.h index 42a8845f..fc306c5b 100644 --- a/cpp/command/image_support.h +++ b/cpp/command/image_support.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2024 Uwe Seimet // diff --git a/cpp/controllers/abstract_controller.cpp b/cpp/controllers/abstract_controller.cpp index 6bb888ca..0ae9a73a 100644 --- a/cpp/controllers/abstract_controller.cpp +++ b/cpp/controllers/abstract_controller.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -15,34 +15,61 @@ AbstractController::AbstractController(Bus &bus, int target_id, int max_luns) : max_luns) { // The initial buffer size is the size of the biggest supported sector - ctrl.buffer.resize(4096); + buffer.resize(4096); device_logger.SetIdAndLun(target_id, -1); } +void AbstractController::CleanUp() const +{ + for (const auto& [_, device] : luns) { + device->CleanUp(); + } +} + +void AbstractController::Reset() +{ + SetPhase(phase_t::busfree); + + offset = 0; + total_length = 0; + current_length = 0; + chunk_size = 0; + + status = status::good; + + initiator_id = UNKNOWN_INITIATOR_ID; + + for (const auto& [_, device] : luns) { + device->Reset(); + } + + bus.Reset(); +} + void AbstractController::SetCurrentLength(int length) { - if (length > static_cast(ctrl.buffer.size())) { - ctrl.buffer.resize(length); + if (length > static_cast(buffer.size())) { + buffer.resize(length); } - ctrl.current_length = length; + current_length = length; } -void AbstractController::SetTransferSize(int length, int chunk_size) +void AbstractController::SetTransferSize(int length, int size) { // The total number of bytes to transfer for the current SCSI/SASI command - ctrl.total_length = length; + total_length = length; // The number of bytes to transfer in a single chunk - ctrl.chunk_size = chunk_size; + chunk_size = size; } void AbstractController::CopyToBuffer(const void *src, size_t size) // NOSONAR Any kind of source data is permitted { SetCurrentLength(static_cast(size)); - memcpy(ctrl.buffer.data(), src, size); + memcpy(buffer.data(), src, size); } unordered_set> AbstractController::GetDevices() const @@ -61,41 +88,32 @@ shared_ptr AbstractController::GetDeviceForLun(int lun) const return it == luns.end() ? nullptr : it->second; } -void AbstractController::Reset() +AbstractController::shutdown_mode AbstractController::ProcessOnController(int ids) { - SetPhase(phase_t::busfree); - - ctrl = { }; - - // Reset all LUNs - for (const auto& [_, device] : luns) { - device->Reset(); - } - - GetBus().Reset(); -} - -void AbstractController::ProcessOnController(int id_data) -{ - device_logger.SetIdAndLun(GetTargetId(), -1); + device_logger.SetIdAndLun(target_id, -1); - const int initiator_id = ExtractInitiatorId(id_data); - if (initiator_id != UNKNOWN_INITIATOR_ID) { - LogTrace(fmt::format("++++ Starting processing for initiator ID {}", initiator_id)); + if (int ids_without_target = ids - (1 << target_id); ids_without_target) { + initiator_id = 0; + while (!(ids_without_target & (1 << initiator_id))) { + ++initiator_id; + } + LogTrace("++++ Starting processing for initiator ID " + to_string(initiator_id)); } else { + initiator_id = UNKNOWN_INITIATOR_ID; LogTrace("++++ Starting processing for unknown initiator ID"); } - while (Process(initiator_id)) { + while (Process()) { // Handle bus phases until the bus is free for the next command } + + return sh_mode; } bool AbstractController::AddDevice(shared_ptr device) { const int lun = device->GetLun(); - if (lun < 0 || lun >= max_luns || GetDeviceForLun(lun) || device->GetController()) { return false; } @@ -112,12 +130,3 @@ bool AbstractController::RemoveDevice(PrimaryDevice &device) return luns.erase(device.GetLun()) == 1; } - -int AbstractController::ExtractInitiatorId(int id_data) const -{ - if (const int id_data_without_target = id_data - (1 << target_id); id_data_without_target) { - return static_cast(log2(id_data_without_target & -id_data_without_target)); - } - - return UNKNOWN_INITIATOR_ID; -} diff --git a/cpp/controllers/abstract_controller.h b/cpp/controllers/abstract_controller.h index da70af2a..fc2eb4d3 100644 --- a/cpp/controllers/abstract_controller.h +++ b/cpp/controllers/abstract_controller.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -24,130 +24,122 @@ class AbstractController : public PhaseHandler public: + enum class shutdown_mode + { + none, + stop_s2p, + stop_pi, + restart_pi + }; + AbstractController(Bus&, int, int); ~AbstractController() override = default; virtual void Error(scsi_defs::sense_key, scsi_defs::asc = scsi_defs::asc::no_additional_sense_information, scsi_defs::status = scsi_defs::status::check_condition) = 0; - virtual void Reset(); - virtual int GetInitiatorId() const = 0; virtual int GetEffectiveLun() const = 0; - enum class shutdown_mode + virtual void Reset(); + + void CleanUp() const; + + int GetInitiatorId() const { - none, - stop_s2p, - stop_pi, - restart_pi - }; + return initiator_id; + } void ScheduleShutdown(shutdown_mode mode) { sh_mode = mode; } - shutdown_mode GetShutdownMode() const - { - return sh_mode; - } int GetTargetId() const { return target_id; } - int GetLunCount() const + auto GetLunCount() const { - return static_cast(luns.size()); + return luns.size(); } unordered_set> GetDevices() const; shared_ptr GetDeviceForLun(int) const; bool AddDevice(shared_ptr); bool RemoveDevice(PrimaryDevice&); - void ProcessOnController(int); + shutdown_mode ProcessOnController(int); void CopyToBuffer(const void*, size_t); auto& GetBuffer() { - return ctrl.buffer; + return buffer; } auto GetStatus() const { - return ctrl.status; - } - void SetStatus(scsi_defs::status s) - { - ctrl.status = s; + return status; } auto GetChunkSize() const { - return ctrl.chunk_size; + return chunk_size; } auto GetCurrentLength() const { - return ctrl.current_length; + return current_length; } void SetCurrentLength(int); void SetTransferSize(int, int); - void SetMessage(int m) - { - ctrl.message = m; - } auto& GetCdb() const { - return ctrl.cdb; + return cdb; } int GetCdbByte(int index) const { - return ctrl.cdb[index]; + return cdb[index]; } - inline static const int UNKNOWN_INITIATOR_ID = -1; + auto GetOpcode() const + { + return static_cast(cdb[0]); + } protected: + virtual bool Process() = 0; + inline Bus& GetBus() const { return bus; } - auto GetOpcode() const - { - return static_cast(ctrl.cdb[0]); - } - int GetLun() const - { - return (ctrl.cdb[1] >> 5) & 0x07; - } - void SetCdbByte(int index, int value) { - ctrl.cdb[index] = value; + cdb[index] = value; } bool UpdateTransferSize() { - ctrl.total_length -= ctrl.chunk_size; - return ctrl.total_length != 0; + total_length -= chunk_size; + return total_length != 0; } - auto GetMessage() const + + void SetStatus(scsi_defs::status s) { - return ctrl.message; + status = s; } auto GetOffset() const { - return ctrl.offset; + return offset; } void ResetOffset() { - ctrl.offset = 0; + offset = 0; } void UpdateOffsetAndLength() { - ctrl.offset += ctrl.current_length; - ctrl.current_length = 0; + offset += current_length; + current_length = 0; } void LogTrace(const string &s) const @@ -165,30 +157,20 @@ class AbstractController : public PhaseHandler private: - int ExtractInitiatorId(int) const; - - using ctrl_t = struct _ctrl_t { - // Command data - array cdb; - - // Status data - scsi_defs::status status; - // Message data - int message; - - // Transfer data buffer, dynamically resized - vector buffer; - // Transfer offset - int offset; - // Total number of bytes to be transferred - int total_length; - // Remaining bytes to be transferred in a single handshake cycle - int current_length; - // The number of bytes to be transferred with a single handshake cycle - int chunk_size; - }; + array cdb = { }; + + // Transfer data buffer, dynamically resized + vector buffer; + // Transfer offset + int offset = 0; + // Total number of bytes to be transferred + int total_length = 0; + // Remaining bytes to be transferred in a single handshake cycle + int current_length = 0; + // The number of bytes to be transferred with the current handshake cycle + int chunk_size = 0; - ctrl_t ctrl = { }; + scsi_defs::status status = status::good; Bus &bus; @@ -199,6 +181,11 @@ class AbstractController : public PhaseHandler int target_id; + static constexpr int UNKNOWN_INITIATOR_ID = -1; + + // The initiator ID may be unavailable, e.g. with Atari ACSI and old host adapters + int initiator_id = UNKNOWN_INITIATOR_ID; + int max_luns; shutdown_mode sh_mode = shutdown_mode::none; diff --git a/cpp/controllers/generic_controller.cpp b/cpp/controllers/controller.cpp similarity index 73% rename from cpp/controllers/generic_controller.cpp rename to cpp/controllers/controller.cpp index 22e594cc..cd2f3792 100644 --- a/cpp/controllers/generic_controller.cpp +++ b/cpp/controllers/controller.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) // Copyright (C) 2014-2020 GIMONS @@ -9,24 +9,26 @@ //--------------------------------------------------------------------------- #include "buses/bus_factory.h" -#include "buses/gpio_bus.h" #ifdef BUILD_MODE_PAGE_DEVICE #include "devices/mode_page_device.h" #endif -#include "generic_controller.h" +#include "shared/shared_exceptions.h" +#include "controller.h" using namespace spdlog; using namespace scsi_defs; using namespace s2p_util; -void GenericController::Reset() +void Controller::Reset() { AbstractController::Reset(); - initiator_id = UNKNOWN_INITIATOR_ID; + identified_lun = -1; + + atn_msg = false; } -bool GenericController::Process(int id) +bool Controller::Process() { GetBus().Acquire(); @@ -36,8 +38,6 @@ bool GenericController::Process(int id) return false; } - initiator_id = id; - if (!ProcessPhase()) { Error(sense_key::aborted_command, asc::controller_process_phase); return false; @@ -46,7 +46,7 @@ bool GenericController::Process(int id) return !IsBusFree(); } -void GenericController::BusFree() +void Controller::BusFree() { if (!IsBusFree()) { LogTrace("BUS FREE phase"); @@ -58,9 +58,11 @@ void GenericController::BusFree() GetBus().SetIO(false); GetBus().SetBSY(false); - // Initialize status and message SetStatus(status::good); - SetMessage(0x00); + + identified_lun = -1; + + atn_msg = false; return; } @@ -70,7 +72,7 @@ void GenericController::BusFree() } } -void GenericController::Selection() +void Controller::Selection() { if (!IsSelection()) { LogTrace("SELECTION phase"); @@ -90,7 +92,7 @@ void GenericController::Selection() } } -void GenericController::Command() +void Controller::Command() { if (!IsCommand()) { LogTrace("COMMAND phase"); @@ -110,7 +112,7 @@ void GenericController::Command() } const int command_bytes_count = BusFactory::Instance().GetCommandBytesCount(static_cast(buf[0])); - assert(command_bytes_count <= 16); + assert(command_bytes_count && command_bytes_count <= 16); for (int i = 0; i < command_bytes_count; i++) { SetCdbByte(i, buf[i]); @@ -128,23 +130,20 @@ void GenericController::Command() return; } - SetCurrentLength(0); - Execute(); } } -void GenericController::Execute() +void Controller::Execute() { + SetCurrentLength(0); ResetOffset(); SetTransferSize(0, 0); const auto opcode = GetOpcode(); auto device = GetDeviceForLun(GetEffectiveLun()); - const bool has_lun = device != nullptr; - - if (!has_lun) { + if (!device) { if (opcode != scsi_command::cmd_inquiry && opcode != scsi_command::cmd_request_sense) { Error(sense_key::illegal_request, asc::invalid_lun); return; @@ -160,34 +159,21 @@ void GenericController::Execute() device->SetStatus(sense_key::no_sense, asc::no_additional_sense_information); } - if (device->CheckReservation(GetInitiatorId(), opcode, GetCdbByte(4) & 0x01)) { + if (device->CheckReservation(GetInitiatorId())) { try { device->Dispatch(opcode); - - // SCSI-2 section 8.2.5.1: Incorrect logical unit handling - if (opcode == scsi_command::cmd_inquiry && !has_lun) { - GetBuffer().data()[0] = 0x7f; - } } catch (const scsi_exception &e) { Error(e.get_sense_key(), e.get_asc()); } } - else { - Error(sense_key::aborted_command, asc::no_additional_sense_information, status::reservation_conflict); - } } -void GenericController::Status() +void Controller::Status() { if (!IsStatus()) { - if (const auto &it_status = STATUS_MAPPING.find(GetStatus()); it_status != STATUS_MAPPING.end()) { - LogTrace(fmt::format("Status phase, status is {0} (status code ${1:02x})", it_status->second, + LogTrace(fmt::format("Status phase, status is {0} (status code ${1:02x})", STATUS_MAPPING.at(GetStatus()), static_cast(GetStatus()))); - } - else { - LogTrace(fmt::format("Status phase, status code is ${:02x}", static_cast(GetStatus()))); - } SetPhase(phase_t::status); @@ -206,7 +192,7 @@ void GenericController::Status() Send(); } -void GenericController::MsgIn() +void Controller::MsgIn() { if (!IsMsgIn()) { LogTrace("MESSAGE IN phase"); @@ -224,7 +210,34 @@ void GenericController::MsgIn() Send(); } -void GenericController::DataIn() +void Controller::MsgOut() +{ + if (!IsMsgOut()) { + LogTrace("MESSAGE OUT phase"); + + // Process the IDENTIFY message + if (IsSelection()) { + atn_msg = true; + msg_bytes.clear(); + } + + SetPhase(phase_t::msgout); + + GetBus().SetMSG(true); + GetBus().SetCD(true); + GetBus().SetIO(false); + + ResetOffset(); + SetCurrentLength(1); + SetTransferSize(1, 1); + + return; + } + + Receive(); +} + +void Controller::DataIn() { if (!IsDataIn()) { if (!GetCurrentLength()) { @@ -247,7 +260,7 @@ void GenericController::DataIn() Send(); } -void GenericController::DataOut() +void Controller::DataOut() { if (!IsDataOut()) { if (!GetCurrentLength()) { @@ -270,7 +283,7 @@ void GenericController::DataOut() Receive(); } -void GenericController::Error(sense_key sense_key, asc asc, scsi_defs::status status) +void Controller::Error(sense_key sense_key, asc asc, scsi_defs::status status) { GetBus().Acquire(); if (GetBus().GetRST() || IsStatus() || IsMsgIn()) { @@ -291,12 +304,11 @@ void GenericController::Error(sense_key sense_key, asc asc, scsi_defs::status st } SetStatus(status); - SetMessage(0x00); Status(); } -void GenericController::Send() +void Controller::Send() { assert(!GetBus().GetREQ()); assert(GetBus().GetIO()); @@ -335,7 +347,6 @@ void GenericController::Send() LogTrace("All data transferred"); - // Move to next phase switch (GetPhase()) { case phase_t::msgin: ProcessExtendedMessage(); @@ -348,7 +359,8 @@ void GenericController::Send() case phase_t::status: SetCurrentLength(1); SetTransferSize(1, 1); - GetBuffer()[0] = (uint8_t)GetMessage(); + // Message byte + GetBuffer()[0] = 0; MsgIn(); break; @@ -358,7 +370,7 @@ void GenericController::Send() } } -void GenericController::Receive() +void Controller::Receive() { assert(!GetBus().GetREQ()); assert(!GetBus().GetIO()); @@ -373,7 +385,7 @@ void GenericController::Receive() } // Assume that data less than < 256 bytes in DATA OUT are parameters to a non block-oriented command else if (IsDataOut() && !GetOffset() && l < 256 && get_level() == level::trace) { - LogTrace(fmt::format("{} byte(s) of command parameter data:\n{}", l, FormatBytes(GetBuffer(), l))); + LogTrace(fmt::format("{0} byte(s) of command parameter data:\n{1}", l, FormatBytes(GetBuffer(), l))); } } @@ -394,9 +406,6 @@ void GenericController::Receive() case phase_t::msgout: XferMsg(GetBuffer()[0]); - - // Clear message data in preparation for MESSAGE IN - SetMessage(0x00); break; default: @@ -410,7 +419,6 @@ void GenericController::Receive() return; } - // Move to next phase switch (GetPhase()) { case phase_t::msgout: ProcessMessage(); @@ -423,22 +431,21 @@ void GenericController::Receive() default: error("Unexpected bus phase: " + Bus::GetPhaseName(GetPhase())); - assert(false); break; } } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" -bool GenericController::XferIn(vector &buf) +bool Controller::XferIn(vector &buf) { // Limited to read commands switch (GetOpcode()) { case scsi_command::cmd_read6: - case scsi_command::cmd_read10: - case scsi_command::cmd_read16: - case scsi_command::cmd_read_long10: - case scsi_command::cmd_read_capacity16_read_long16: + case scsi_command::cmd_read10: + case scsi_command::cmd_read16: + case scsi_command::cmd_read_long10: + case scsi_command::cmd_read_capacity16_read_long16: try { SetCurrentLength(GetDeviceForLun(GetEffectiveLun())->ReadData(buf)); } @@ -463,14 +470,14 @@ bool GenericController::XferIn(vector &buf) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" -bool GenericController::XferOut(bool cont) +bool Controller::XferOut(bool cont) { auto device = GetDeviceForLun(GetEffectiveLun()); // Limited to write/verify commands switch (const auto opcode = GetOpcode(); opcode) { case scsi_command::cmd_mode_select6: - case scsi_command::cmd_mode_select10: + case scsi_command::cmd_mode_select10: { #ifdef BUILD_MODE_PAGE_DEVICE auto mode_page_device = dynamic_pointer_cast(device); @@ -481,7 +488,7 @@ bool GenericController::XferOut(bool cont) } try { - mode_page_device->ModeSelect(GetOpcode(), GetCdb(), GetBuffer(), GetOffset()); + mode_page_device->ModeSelect(opcode, GetCdb(), GetBuffer(), GetOffset()); } catch (const scsi_exception &e) { Error(e.get_sense_key(), e.get_asc()); @@ -494,13 +501,13 @@ bool GenericController::XferOut(bool cont) break; case scsi_command::cmd_write6: - case scsi_command::cmd_write10: - case scsi_command::cmd_write16: - case scsi_command::cmd_verify10: - case scsi_command::cmd_verify16: - case scsi_command::cmd_write_long10: - case scsi_command::cmd_write_long16: - case scsi_command::cmd_execute_operation: + case scsi_command::cmd_write10: + case scsi_command::cmd_write16: + case scsi_command::cmd_verify10: + case scsi_command::cmd_verify16: + case scsi_command::cmd_write_long10: + case scsi_command::cmd_write_long16: + case scsi_command::cmd_execute_operation: { try { const auto length = device->WriteData(GetBuffer(), opcode); @@ -530,12 +537,96 @@ bool GenericController::XferOut(bool cont) } #pragma GCC diagnostic pop -void GenericController::LogCdb() const +void Controller::XferMsg(uint8_t msg) +{ + assert(IsMsgOut()); + + if (atn_msg) { + msg_bytes.emplace_back(msg); + } +} + +void Controller::ParseMessage() +{ + for (const uint8_t message : msg_bytes) { + switch (message) { + case 0x01: { + LogTrace("Received EXTENDED MESSAGE"); + SetCurrentLength(1); + SetTransferSize(1, 1); + // MESSSAGE REJECT + GetBuffer()[0] = 0x07; + MsgIn(); + return; + } + + case 0x06: { + LogTrace("Received ABORT message"); + BusFree(); + return; + } + + case 0x0c: { + LogTrace("Received BUS DEVICE RESET message"); + if (auto device = GetDeviceForLun(GetEffectiveLun()); device) { + device->SetReset(true); + device->DiscardReservation(); + } + BusFree(); + return; + } + + default: + if (message >= 0x80) { + identified_lun = static_cast(message) & 0x1f; + LogTrace("Received IDENTIFY message for LUN " + to_string(identified_lun)); + } + break; + } + } +} + +void Controller::ProcessMessage() +{ + // MESSAGE OUT phase as long as ATN is asserted + if (GetBus().GetATN()) { + ResetOffset(); + SetCurrentLength(1); + SetTransferSize(1, 1); + return; + } + + if (atn_msg) { + atn_msg = false; + ParseMessage(); + } + + Command(); +} + +void Controller::ProcessExtendedMessage() +{ + // Completed sending response to extended message of IDENTIFY message + if (atn_msg) { + atn_msg = false; + Command(); + } else { + BusFree(); + } +} + +int Controller::GetEffectiveLun() const +{ + // Return LUN from IDENTIFY message, or return the LUN from the CDB as fallback + return identified_lun != -1 ? identified_lun : GetCdbByte(1) >> 5; +} + +void Controller::LogCdb() const { const string &command_name = BusFactory::Instance().GetCommandName(GetOpcode()); string s = fmt::format("Controller is executing {}, CDB $", !command_name.empty() ? command_name : fmt::format("{:02x}", GetCdbByte(0))); - for (int i = 0; i < BusFactory::Instance().GetCommandBytesCount(static_cast(GetCdbByte(0))); i++) { + for (int i = 0; i < BusFactory::Instance().GetCommandBytesCount(GetOpcode()); i++) { if (i) { s += ":"; } diff --git a/cpp/controllers/generic_controller.h b/cpp/controllers/controller.h similarity index 59% rename from cpp/controllers/generic_controller.h rename to cpp/controllers/controller.h index a1691cda..13980830 100644 --- a/cpp/controllers/generic_controller.h +++ b/cpp/controllers/controller.h @@ -1,65 +1,60 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2024 Uwe Seimet // -// Abstract base class for SCSI-like controllers -// //--------------------------------------------------------------------------- #pragma once #include "abstract_controller.h" -class GenericController : public AbstractController +class Controller : public AbstractController { public: using AbstractController::AbstractController; - ~GenericController() override = default; - - void Reset() override; + ~Controller() override = default; - bool Process(int) override; + bool Process() override; void Error(scsi_defs::sense_key sense_key, scsi_defs::asc asc = scsi_defs::asc::no_additional_sense_information, scsi_defs::status status = scsi_defs::status::check_condition) override; - - int GetInitiatorId() const override - { - return initiator_id; - } + void Reset() override; void BusFree() override; void Selection() override; void Command() override; void MsgIn() override; + void MsgOut() override; void Status() override; void DataIn() override; void DataOut() override; -protected: + int GetEffectiveLun() const override; - bool XferIn(vector&); - bool XferOut(bool); - void Receive(); +private: void Execute(); + void Send(); + void Receive(); + void XferMsg(uint8_t); + bool XferIn(vector&); + bool XferOut(bool); - virtual void ParseMessage() = 0; - virtual void ProcessMessage() = 0; - virtual void ProcessExtendedMessage() = 0; + void ParseMessage(); + void ProcessMessage(); + void ProcessExtendedMessage(); -private: + void LogCdb() const; - void Send(); - virtual void XferMsg(int) = 0; + // The LUN from the IDENTIFY message + int identified_lun = -1; - void LogCdb() const; + bool atn_msg = false; - // The initiator ID may be unavailable, e.g. with Atari ACSI and old host adapters - int initiator_id = UNKNOWN_INITIATOR_ID; + vector msg_bytes; }; diff --git a/cpp/controllers/controller_factory.cpp b/cpp/controllers/controller_factory.cpp index 7d47d86e..a33d7056 100644 --- a/cpp/controllers/controller_factory.cpp +++ b/cpp/controllers/controller_factory.cpp @@ -1,27 +1,20 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // //--------------------------------------------------------------------------- -#include "scsi_controller.h" -#include "sasi_controller.h" +#include "controller.h" #include "controller_factory.h" using namespace std; shared_ptr ControllerFactory::CreateController(Bus &bus, int id) const { - shared_ptr controller; - if (is_sasi) { - controller = make_shared(bus, id, GetSasiLunMax()); - } - else { - controller = make_shared(bus, id, GetScsiLunMax()); - } - + shared_ptr controller = make_shared(bus, id, + is_sasi ? GetSasiLunMax() : GetScsiLunMax()); controller->Init(); return controller; @@ -55,40 +48,31 @@ bool ControllerFactory::AttachToController(Bus &bus, int id, shared_ptrCleanUp(); - } + controller.CleanUp(); return controllers.erase(controller.GetTargetId()) == 1; } bool ControllerFactory::DeleteAllControllers() { - bool has_controller = false; - - unordered_set> values; - ranges::transform(controllers, inserter(values, values.begin()), [](const auto &controller) { - return controller.second; - }); - - for (const auto &controller : values) { - DeleteController(*controller); - has_controller = true; + if (controllers.empty()) { + return false; } - assert(controllers.empty()); + for (auto it = controllers.cbegin(); it != controllers.cend();) { + (*it).second->CleanUp(); + controllers.erase(it++); + } - return has_controller; + return true; } -AbstractController::shutdown_mode ControllerFactory::ProcessOnController(int id_data) const +AbstractController::shutdown_mode ControllerFactory::ProcessOnController(int ids) const { if (const auto &it = ranges::find_if(controllers, [&](const auto &c) { - return (id_data & (1 << c.first)); + return (ids & (1 << c.first)); }); it != controllers.end()) { - (*it).second->ProcessOnController(id_data); - - return (*it).second->GetShutdownMode(); + return (*it).second->ProcessOnController(ids); } return AbstractController::shutdown_mode::none; @@ -111,7 +95,7 @@ unordered_set> ControllerFactory::GetAllDevices() cons for (const auto& [_, controller] : controllers) { const auto &d = controller->GetDevices(); - devices.insert(d.begin(), d.end()); + devices.insert(d.cbegin(), d.cend()); } return devices; diff --git a/cpp/controllers/controller_factory.h b/cpp/controllers/controller_factory.h index 6ff29ee1..10cfc268 100644 --- a/cpp/controllers/controller_factory.h +++ b/cpp/controllers/controller_factory.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -17,7 +17,7 @@ class ControllerFactory public: - explicit ControllerFactory(bool b = false) + explicit ControllerFactory(bool b) { is_sasi = b; } diff --git a/cpp/controllers/phase_handler.cpp b/cpp/controllers/phase_handler.cpp index 0f36f4c8..902627e1 100644 --- a/cpp/controllers/phase_handler.cpp +++ b/cpp/controllers/phase_handler.cpp @@ -1,23 +1,27 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2023-2024 Uwe Seimet // //--------------------------------------------------------------------------- +#include #include "phase_handler.h" using namespace scsi_defs; void PhaseHandler::Init() { - phase_executors[phase_t::busfree] = [this]() {BusFree();}; - phase_executors[phase_t::selection] = [this]() {Selection();}; - phase_executors[phase_t::dataout] = [this]() {DataOut();}; - phase_executors[phase_t::datain] = [this]() {DataIn();}; - phase_executors[phase_t::command] = [this]() {Command();}; - phase_executors[phase_t::status] = [this]() {Status();}; - phase_executors[phase_t::msgout] = [this]() {MsgOut();}; - phase_executors[phase_t::msgin] = [this]() {MsgIn();}; + phase_executors[static_cast(phase_t::busfree)] = [this]() {BusFree();}; + phase_executors[static_cast(phase_t::arbitration)] = []() {throw invalid_argument("");}; + phase_executors[static_cast(phase_t::selection)] = [this]() {Selection();}; + phase_executors[static_cast(phase_t::reselection)] = []() {throw invalid_argument("");}; + phase_executors[static_cast(phase_t::command)] = [this]() {Command();}; + phase_executors[static_cast(phase_t::datain)] = [this]() {DataIn();}; + phase_executors[static_cast(phase_t::dataout)] = [this]() {DataOut();}; + phase_executors[static_cast(phase_t::status)] = [this]() {Status();}; + phase_executors[static_cast(phase_t::msgin)] = [this]() {MsgIn();}; + phase_executors[static_cast(phase_t::msgout)] = [this]() {MsgOut();}; + phase_executors[static_cast(phase_t::reserved)] = []() {throw invalid_argument("");}; } diff --git a/cpp/controllers/phase_handler.h b/cpp/controllers/phase_handler.h index f76c9834..cb1d0243 100644 --- a/cpp/controllers/phase_handler.h +++ b/cpp/controllers/phase_handler.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -8,16 +8,14 @@ #pragma once -#include +#include #include #include "shared/scsi.h" -#include "shared/shared_exceptions.h" using namespace scsi_defs; class PhaseHandler { - phase_t phase = phase_t::busfree; public: @@ -33,52 +31,47 @@ class PhaseHandler virtual void DataIn() = 0; virtual void DataOut() = 0; virtual void MsgIn() = 0; - virtual void MsgOut() - { - // To be implemented by controllers supporting this phase (SCSI, but not SASI) - } - - virtual bool Process(int) = 0; + virtual void MsgOut() = 0; protected: - phase_t GetPhase() const + inline phase_t GetPhase() const { return phase; } - void SetPhase(phase_t p) + inline void SetPhase(phase_t p) { phase = p; } - bool IsSelection() const + inline bool IsSelection() const { return phase == phase_t::selection; } - bool IsBusFree() const + inline bool IsBusFree() const { return phase == phase_t::busfree; } - bool IsCommand() const + inline bool IsCommand() const { return phase == phase_t::command; } - bool IsStatus() const + inline bool IsStatus() const { return phase == phase_t::status; } - bool IsDataIn() const + inline bool IsDataIn() const { return phase == phase_t::datain; } - bool IsDataOut() const + inline bool IsDataOut() const { return phase == phase_t::dataout; } - bool IsMsgIn() const + inline bool IsMsgIn() const { return phase == phase_t::msgin; } - bool IsMsgOut() const + inline bool IsMsgOut() const { return phase == phase_t::msgout; } @@ -86,9 +79,9 @@ class PhaseHandler bool ProcessPhase() const { try { - phase_executors.at(phase)(); + phase_executors[static_cast(phase)](); } - catch (const out_of_range&) { + catch (const invalid_argument&) { return false; } @@ -97,5 +90,7 @@ class PhaseHandler private: - unordered_map> phase_executors; + phase_t phase = phase_t::busfree; + + array, 11> phase_executors; }; diff --git a/cpp/controllers/sasi_controller.cpp b/cpp/controllers/sasi_controller.cpp deleted file mode 100644 index e44d3fae..00000000 --- a/cpp/controllers/sasi_controller.cpp +++ /dev/null @@ -1,40 +0,0 @@ -//--------------------------------------------------------------------------- -// -// SCSI target emulator and SCSI tools for the Raspberry Pi -// -// Copyright (C) 2023 Uwe Seimet -// -//--------------------------------------------------------------------------- - -#include "sasi_controller.h" - -void SasiController::MsgOut() -{ - // SASI does not support a message out phase - Command(); -} - -void SasiController::XferMsg(int) -{ - assert(false); -} - -void SasiController::ParseMessage() -{ - assert(false); -} - -void SasiController::ProcessMessage() -{ - assert(false); -} - -void SasiController::ProcessExtendedMessage() -{ - BusFree(); -} - -int SasiController::GetEffectiveLun() const -{ - return GetLun(); -} diff --git a/cpp/controllers/sasi_controller.h b/cpp/controllers/sasi_controller.h deleted file mode 100644 index e86d01ea..00000000 --- a/cpp/controllers/sasi_controller.h +++ /dev/null @@ -1,32 +0,0 @@ -//--------------------------------------------------------------------------- -// -// SCSI target emulator and SCSI tools for the Raspberry Pi -// -// Copyright (C) 2023 Uwe Seimet -// -//--------------------------------------------------------------------------- - -#pragma once - -#include "generic_controller.h" - -class SasiController : public GenericController -{ - -public: - - using GenericController::GenericController; - ~SasiController() override = default; - - int GetEffectiveLun() const override; - - void MsgOut() override; - -private: - - void XferMsg(int) override; - - void ParseMessage() override; - void ProcessMessage() override; - void ProcessExtendedMessage() override; -}; diff --git a/cpp/controllers/scsi_controller.cpp b/cpp/controllers/scsi_controller.cpp deleted file mode 100644 index 28400908..00000000 --- a/cpp/controllers/scsi_controller.cpp +++ /dev/null @@ -1,152 +0,0 @@ -//--------------------------------------------------------------------------- -// -// SCSI target emulator and SCSI tools for the Raspberry Pi -// -// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) -// Copyright (C) 2014-2020 GIMONS -// Copyright (C) 2022-2024 Uwe Seimet -// -//--------------------------------------------------------------------------- - -#include "base/primary_device.h" -#include "scsi_controller.h" - -void ScsiController::Reset() -{ - GenericController::Reset(); - - identified_lun = -1; - - atn_msg = false; -} - -void ScsiController::BusFree() -{ - if (!IsBusFree()) { - // Initialize ATN message reception status - atn_msg = false; - - identified_lun = -1; - } - - GenericController::BusFree(); -} - -void ScsiController::MsgOut() -{ - if (!IsMsgOut()) { - LogTrace("MESSAGE OUT phase"); - - // Process the IDENTIFY message - if (IsSelection()) { - atn_msg = true; - msc = 0; - msb = { }; - } - - SetPhase(phase_t::msgout); - - GetBus().SetMSG(true); - GetBus().SetCD(true); - GetBus().SetIO(false); - - ResetOffset(); - SetCurrentLength(1); - SetTransferSize(1, 1); - - return; - } - - Receive(); -} - -void ScsiController::XferMsg(int msg) -{ - assert(IsMsgOut()); - - // Save message out data - if (atn_msg) { - msb[msc] = (uint8_t)msg; - msc++; - msc %= 256; - } -} - -void ScsiController::ParseMessage() -{ - int count = -1; - while (++count < msc) { - const uint8_t message = msb[count]; - switch (message) { - case 0x01: { - LogTrace("Received EXTENDED MESSAGE"); - SetCurrentLength(1); - SetTransferSize(1, 1); - // MESSSAGE REJECT - GetBuffer()[0] = 0x07; - MsgIn(); - return; - } - - case 0x06: { - LogTrace("Received ABORT message"); - BusFree(); - return; - } - - case 0x0c: { - LogTrace("Received BUS DEVICE RESET message"); - if (auto device = GetDeviceForLun(identified_lun); device) { - device->DiscardReservation(); - } - BusFree(); - return; - } - - default: - if (message >= 0x80) { - identified_lun = static_cast(message) & 0x1f; - LogTrace(fmt::format("Received IDENTIFY message for LUN {}", identified_lun)); - } - break; - } - } -} - -void ScsiController::ProcessMessage() -{ - // Continue message out phase as long as ATN keeps asserting - if (GetBus().GetATN()) { - ResetOffset(); - SetCurrentLength(1); - SetTransferSize(1, 1); - return; - } - - if (atn_msg) { - ParseMessage(); - } - - // Initialize ATN message reception status - atn_msg = false; - - Command(); -} - -void ScsiController::ProcessExtendedMessage() -{ - // Completed sending response to extended message of IDENTIFY message - if (atn_msg) { - atn_msg = false; - - Command(); - } else { - BusFree(); - } -} - -int ScsiController::GetEffectiveLun() const -{ - // Return LUN from IDENTIFY message, or return the LUN from the CDB as fallback - return identified_lun != -1 ? identified_lun : GetLun(); -} diff --git a/cpp/controllers/scsi_controller.h b/cpp/controllers/scsi_controller.h deleted file mode 100644 index dd33503d..00000000 --- a/cpp/controllers/scsi_controller.h +++ /dev/null @@ -1,51 +0,0 @@ -//--------------------------------------------------------------------------- -// -// SCSI target emulator and SCSI tools for the Raspberry Pi -// -// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) -// Copyright (C) 2014-2020 GIMONS -// Copyright (C) akuker -// Copyright (C) 2021-2023 Uwe Seimet -// -//--------------------------------------------------------------------------- - -#pragma once - -#include -#include "generic_controller.h" - -using namespace std; - -class ScsiController : public GenericController -{ - -public: - - using GenericController::GenericController; - ~ScsiController() override = default; - - void Reset() override; - - int GetEffectiveLun() const override; - - void BusFree() override; - void MsgOut() override; - -private: - - void XferMsg(int) override; - - void ParseMessage() override; - void ProcessMessage() override; - void ProcessExtendedMessage() override; - - // The LUN from the IDENTIFY message - int identified_lun = -1; - - bool atn_msg = false; - - // Message bytes and counter - array msb = { }; - int msc = 0; -}; - diff --git a/cpp/devices/cache.h b/cpp/devices/cache.h index dff1dacc..d08a98a0 100644 --- a/cpp/devices/cache.h +++ b/cpp/devices/cache.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2024 Uwe Seimet // diff --git a/cpp/devices/daynaport.cpp b/cpp/devices/daynaport.cpp index 9b4bd932..d4d593db 100644 --- a/cpp/devices/daynaport.cpp +++ b/cpp/devices/daynaport.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2014-2020 GIMONS // Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) @@ -60,7 +60,7 @@ bool DaynaPort::Init(const param_map ¶ms) }); AddCommand(scsi_command::cmd_retrieve_stats, [this] { - RetrieveStatistics(); + RetrieveStats(); }); AddCommand(scsi_command::cmd_set_iface_mode, [this] { @@ -83,9 +83,7 @@ bool DaynaPort::Init(const param_map ¶ms) #endif } - Reset(); SetReady(true); - SetReset(false); return true; } @@ -142,8 +140,8 @@ vector DaynaPort::InquiryInternal() const //--------------------------------------------------------------------------- int DaynaPort::Read(cdb_t cdb, vector &buf, uint64_t) { - // At startup the host may send a READ(6) command with a sector count of 1 to read the root sector. - // This will trigger a SCSI error message. + // At startup a driver may send a READ(6) command with a sector count of 1 to read the root sector. + // The code below will intentionally trigger a SCSI error message in this case. if (cdb[4] == 1) { return 0; } @@ -251,8 +249,10 @@ int DaynaPort::WriteData(span buf, scsi_command command) // - long #3: frames lost // //--------------------------------------------------------------------------- -int DaynaPort::RetrieveStats(cdb_t cdb, vector &buf) const +void DaynaPort::RetrieveStats() const { + auto &buf = GetController()->GetBuffer(); + memcpy(buf.data(), &m_scsi_link_stats, sizeof(m_scsi_link_stats)); // Take the last 3 MAC address bytes from the bridge's MAC address, so that several DaynaPort emulations @@ -266,7 +266,11 @@ int DaynaPort::RetrieveStats(cdb_t cdb, vector &buf) const LogDebug(fmt::format("The DaynaPort MAC address is {0:02x}:{1:02x}:{2:02x}:{3:02x}:{4:02x}:{5:02x}", buf.data()[0], buf.data()[1], buf.data()[2], buf.data()[3], buf.data()[4], buf.data()[5])); - return static_cast(min(sizeof(m_scsi_link_stats), static_cast(GetInt16(cdb, 3)))); + const auto length = static_cast(min(sizeof(m_scsi_link_stats), + static_cast(GetInt16(GetController()->GetCdb(), 3)))); + GetController()->SetTransferSize(length, length); + + DataInPhase(length); } void DaynaPort::TestUnitReady() @@ -311,14 +315,6 @@ void DaynaPort::SendMessage6() const DataOutPhase(length); } -void DaynaPort::RetrieveStatistics() const -{ - const auto length = RetrieveStats(GetController()->GetCdb(), GetController()->GetBuffer()); - GetController()->SetTransferSize(length, length); - - DataInPhase(length); -} - //--------------------------------------------------------------------------- // // Set interface mode/Set MAC address diff --git a/cpp/devices/daynaport.h b/cpp/devices/daynaport.h index 9df61c5a..8b9a67bc 100644 --- a/cpp/devices/daynaport.h +++ b/cpp/devices/daynaport.h @@ -1,11 +1,11 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2020 akuker // Copyright (C) 2014-2020 GIMONS // Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) -// Copyright (C) 2023 Uwe Seimet +// Copyright (C) 2023-2024 Uwe Seimet // // This design is derived from the SLINKCMD.TXT file, as well as David Kuder's // Tiny SCSI Emulator @@ -16,9 +16,6 @@ // this development. (Farallon's EtherMac is a re-branded DaynaPort // SCSI/Link-T). // -// This does NOT include the file system functionality that is present -// in the Sharp X68000 host bridge. -// // Note: This requires a DaynaPort SCSI Link driver. It has successfully been tested with MacOS and the Atari. // //--------------------------------------------------------------------------- @@ -36,11 +33,6 @@ class DaynaPort : public PrimaryDevice, private ScsiCommunicationsCommands { - uint64_t byte_read_count = 0; - uint64_t byte_write_count = 0; - - inline static const string BYTE_READ_COUNT = "byte_read_count"; - inline static const string BYTE_WRITE_COUNT = "byte_write_count"; public: @@ -55,39 +47,30 @@ class DaynaPort : public PrimaryDevice, private ScsiCommunicationsCommands return tap.GetDefaultParams(); } - // Commands vector InquiryInternal() const override; int Read(cdb_t, vector&, uint64_t); int WriteData(span, scsi_command) override; - int RetrieveStats(cdb_t, vector&) const; - void TestUnitReady() override; void GetMessage6() override; void SendMessage6() const override; - void RetrieveStatistics() const; + void RetrieveStats() const; void SetInterfaceMode() const; void SetMcastAddr() const; void EnableInterface() const; vector GetStatistics() const override; - static const int CMD_SCSILINK_STATS = 0x09; - static const int CMD_SCSILINK_ENABLE = 0x0E; - static const int CMD_SCSILINK_SET = 0x0C; - static const int CMD_SCSILINK_SETMAC = 0x40; - static const int CMD_SCSILINK_SETMODE = 0x80; - - // When we're reading the Linux tap device, most of the messages will not be for us, so we - // need to filter through those. However, we don't want to keep re-reading the packets - // indefinitely. So, we'll pick a large-ish number that will cause the emulated DaynaPort - // to respond with "no data" after MAX_READ_RETRIES tries. - static const int MAX_READ_RETRIES = 50; + static constexpr int CMD_SCSILINK_STATS = 0x09; + static constexpr int CMD_SCSILINK_ENABLE = 0x0e; + static constexpr int CMD_SCSILINK_SET = 0x0c; + static constexpr int CMD_SCSILINK_SETMAC = 0x40; + static constexpr int CMD_SCSILINK_SETMODE = 0x80; // The READ response has a header which consists of: // 2 bytes - payload size // 4 bytes - status flags - static const uint32_t DAYNAPORT_READ_HEADER_SZ = 2 + 4; + static constexpr uint32_t DAYNAPORT_READ_HEADER_SZ = 2 + 4; private: @@ -123,4 +106,10 @@ class DaynaPort : public PrimaryDevice, private ScsiCommunicationsCommands TapDriver tap; bool tap_enabled = false; + + uint64_t byte_read_count = 0; + uint64_t byte_write_count = 0; + + inline static const string BYTE_READ_COUNT = "byte_read_count"; + inline static const string BYTE_WRITE_COUNT = "byte_write_count"; }; diff --git a/cpp/devices/disk.cpp b/cpp/devices/disk.cpp index 55b1622d..2ba581f8 100644 --- a/cpp/devices/disk.cpp +++ b/cpp/devices/disk.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) // Copyright (C) 2014-2020 GIMONS @@ -18,6 +18,7 @@ #include "disk_cache.h" #include "disk.h" +using namespace spdlog; using namespace memory_util; using namespace s2p_util; @@ -137,10 +138,18 @@ void Disk::Dispatch(scsi_command cmd) SetMediumChanged(false); - GetController()->Error(sense_key::unit_attention, asc::not_ready_to_ready_change); + throw scsi_exception(sense_key::unit_attention, asc::not_ready_to_ready_change); } - else { - PrimaryDevice::Dispatch(cmd); + + StorageDevice::Dispatch(cmd); +} + +void Disk::ValidateFile() +{ + StorageDevice::ValidateFile(); + + if (!SetUpCache()) { + throw io_exception("Can't initialize cache"); } } @@ -149,11 +158,11 @@ bool Disk::SetUpCache() assert(caching_mode != PbCachingMode::DEFAULT); if (!supported_sector_sizes.contains(sector_size)) { - spdlog::warn("Using non-standard sector size of {} bytes", sector_size); - + warn("Using non-standard sector size of {} bytes", sector_size); if (caching_mode == PbCachingMode::PISCSI) { caching_mode = PbCachingMode::LINUX; - spdlog::info("Adjusted caching mode to '{}'", ToLower(PbCachingMode_Name(caching_mode))); + // LogInfo() does not work here because at initialization time the device ID is not yet set + info("Switched caching mode to '{}'", PbCachingMode_Name(caching_mode)); } } @@ -276,10 +285,12 @@ void Disk::ReadWriteLong(uint64_t sector, uint32_t length, bool write) auto linux_cache = dynamic_pointer_cast(cache); if (!linux_cache) { - LogWarn( - "Full READ/WRITE LONG support requires a different caching mode than '" - + ToLower(PbCachingMode_Name(caching_mode)) + "'"); - throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb); + // FUll READ/WRITE LONG support requires an appropriate caching mode + FlushCache(); + caching_mode = PbCachingMode::LINUX; + InitCache(GetFilename()); + linux_cache = dynamic_pointer_cast(cache); + LogInfo(fmt::format("Switched caching mode to '{}'", PbCachingMode_Name(caching_mode))); } if (length > sector_size) { @@ -335,6 +346,15 @@ void Disk::StartStopUnit() FlushCache(); } } + else if (load && !last_filename.empty()) { + SetFilename(last_filename); + if (!ReserveFile()) { + last_filename.clear(); + throw scsi_exception(sense_key::illegal_request, asc::load_or_eject_failed); + } + + SetMediumChanged(true); + } StatusPhase(); } @@ -379,6 +399,8 @@ bool Disk::Eject(bool force) FlushCache(); cache.reset(); + last_filename = GetFilename(); + // The image file for this drive is not in use anymore UnreserveFile(); @@ -610,9 +632,10 @@ void Disk::ModeSelect(scsi_command cmd, cdb_t cdb, span buf, int int size = GetSectorSizeInBytes(); - int offset = EvaluateBlockDescriptors(cmd, buf, length, size); + int offset = EvaluateBlockDescriptors(cmd, span(buf.data(), length), size); length -= offset; + // Set up the available pages in order to check for the right page size below map> pages; SetUpModePages(pages, 0x3f, true); @@ -628,71 +651,62 @@ void Disk::ModeSelect(scsi_command cmd, cdb_t cdb, span buf, int if (length < 2) { throw scsi_exception(sense_key::illegal_request, asc::parameter_list_length_error); } - const size_t page_size = buf[offset + 1]; + + // The page size field does not count itself and the page code field + const size_t page_size = buf[offset + 1] + 2; // The page size in the parameters must match the actual page size - if (it->second.size() - 2 != page_size || page_size + 2 > static_cast(length)) { + if (it->second.size() != page_size || page_size > static_cast(length)) { throw scsi_exception(sense_key::illegal_request, asc::parameter_list_length_error); } switch (page_code) { - // Read-write error recovery page + // Read-write/Verify error recovery and caching pages case 0x01: - // Simply ignore the requested changes in the error handling, they are not relevant for SCSI2Pi + case 0x07: + case 0x08: + // Simply ignore the requested changes in the error handling or caching, they are not relevant for SCSI2Pi break; // Format device page - case 0x03: { + case 0x03: // With this page the sector size for a subsequent FORMAT can be selected, but only a few drives // support this, e.g. FUJITSU M2624S. // We are fine as long as the permanent current sector size remains unchanged. VerifySectorSizeChange(GetInt16(buf, offset + 12), false); break; - } - - // Verify error recovery page - case 0x07: - // Simply ignore the requested changes in the error handling, they are not relevant for SCSI2Pi - break; default: throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_parameter_list); - break; } - // The page size field does not count itself and the page code field - length -= page_size + 2; - offset += page_size + 2; + length -= page_size; + offset += page_size; } ChangeSectorSize(size); } -int Disk::EvaluateBlockDescriptors(scsi_command cmd, span buf, int length, int &size) const +int Disk::EvaluateBlockDescriptors(scsi_command cmd, span buf, int &size) const { assert(cmd == scsi_command::cmd_mode_select6 || cmd == scsi_command::cmd_mode_select10); - int required_length; - int block_descriptor_length; - if (cmd == scsi_command::cmd_mode_select10) { - block_descriptor_length = GetInt16(buf, 6); - required_length = 8; - } - else { - block_descriptor_length = buf[3]; - required_length = 4; + const size_t required_length = cmd == scsi_command::cmd_mode_select10 ? 8 : 4; + if (buf.size() < required_length) { + throw scsi_exception(sense_key::illegal_request, asc::parameter_list_length_error); } - if (length < block_descriptor_length + required_length) { + const size_t descriptor_length = cmd == scsi_command::cmd_mode_select10 ? GetInt16(buf, 6) : buf[3]; + if (buf.size() < descriptor_length + required_length) { throw scsi_exception(sense_key::illegal_request, asc::parameter_list_length_error); } // Check for temporary sector size change in first block descriptor - if (block_descriptor_length && length >= required_length + 8) { - size = VerifySectorSizeChange(GetInt16(buf, required_length + 6), true); + if (descriptor_length && buf.size() >= required_length + 8) { + size = VerifySectorSizeChange(GetInt16(buf, static_cast(required_length) + 6), true); } - return block_descriptor_length + required_length; + return static_cast(descriptor_length + required_length); } int Disk::VerifySectorSizeChange(int requested_size, bool temporary) const @@ -800,15 +814,9 @@ void Disk::ReadCapacity10() vector &buf = GetController()->GetBuffer(); - // Create end of logical block address (blocks-1) - uint64_t capacity = GetBlockCount() - 1; - // If the capacity exceeds 32 bit, -1 must be returned and the client has to use READ CAPACITY(16) - if (capacity > 4294967295) { - capacity = -1; - } - SetInt32(buf, 0, static_cast(capacity)); - + const uint64_t capacity = GetBlockCount() - 1; + SetInt32(buf, 0, static_cast(capacity > 0xffffffff ? -1 : capacity)); SetInt32(buf, 4, sector_size); DataInPhase(8); @@ -823,19 +831,12 @@ void Disk::ReadCapacity16() } vector &buf = GetController()->GetBuffer(); + fill_n(buf.begin(), 32, 0); - // Create end of logical block address (blocks-1) SetInt64(buf, 0, GetBlockCount() - 1); - - // Create block length (1 << size) SetInt32(buf, 8, sector_size); - buf[12] = 0; - - // Logical blocks per physical block: not reported (1 or more) - buf[13] = 0; - - DataInPhase(14); + DataInPhase(min(32, static_cast(GetInt32(GetController()->GetCdb(), 10)))); } void Disk::ReadCapacity16_ReadLong16() @@ -885,7 +886,7 @@ tuple Disk::CheckAndGetStartAndCount(access_mode mode) count = GetController()->GetCdbByte(4); if (!count) { - count = 0x100; + count = 256; } } else { @@ -905,7 +906,7 @@ tuple Disk::CheckAndGetStartAndCount(access_mode mode) LogTrace(fmt::format("READ/WRITE/VERIFY/SEEK, start sector: {0}, sector count: {1}", start, count)); // Check capacity - if (uint64_t capacity = GetBlockCount(); !capacity || start > capacity || start + count > capacity) { + if (const uint64_t capacity = GetBlockCount(); !capacity || start + count > capacity) { LogTrace( fmt::format("Capacity of {0} sector(s) exceeded: Trying to access sector {1}, sector count {2}", capacity, start, count)); @@ -913,11 +914,7 @@ tuple Disk::CheckAndGetStartAndCount(access_mode mode) } // Do not process 0 blocks - if (!count && (mode != SEEK6 && mode != SEEK10)) { - return tuple(false, start, count); - } - - return tuple(true, start, count); + return tuple(count || mode == SEEK6 || mode == SEEK10, start, count); } void Disk::ChangeSectorSize(uint32_t new_size) diff --git a/cpp/devices/disk.h b/cpp/devices/disk.h index 81658185..10590b36 100644 --- a/cpp/devices/disk.h +++ b/cpp/devices/disk.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) // Copyright (C) 2014-2020 GIMONS @@ -27,8 +27,9 @@ class Disk : public StorageDevice, private ScsiBlockCommands public: - Disk(PbDeviceType type, scsi_level level, int lun, bool supports_mode_pages, const unordered_set &s) - : StorageDevice(type, level, lun, supports_mode_pages), supported_sector_sizes(s) + Disk(PbDeviceType type, scsi_level level, int lun, bool supports_mode_select, bool supports_save_parameters, + const unordered_set &s) + : StorageDevice(type, level, lun, supports_mode_select, supports_save_parameters), supported_sector_sizes(s) { } ~Disk() override = default; @@ -76,7 +77,8 @@ class Disk : public StorageDevice, private ScsiBlockCommands protected: - bool SetUpCache(); + void ValidateFile() override; + bool InitCache(const string&); void SetUpModePages(map>&, int, bool) const override; @@ -88,7 +90,7 @@ class Disk : public StorageDevice, private ScsiBlockCommands void AddAppleVendorPage(map>&, bool) const; void ModeSelect(scsi_defs::scsi_command, cdb_t, span, int) override; - int EvaluateBlockDescriptors(scsi_defs::scsi_command, span, int, int&) const; + int EvaluateBlockDescriptors(scsi_defs::scsi_command, span, int&) const; int VerifySectorSizeChange(int, bool) const; void ChangeSectorSize(uint32_t); @@ -152,6 +154,8 @@ class Disk : public StorageDevice, private ScsiBlockCommands void WriteLong16(); void ReadCapacity16_ReadLong16(); + bool SetUpCache(); + void ReadWriteLong(uint64_t, uint32_t, bool); void WriteVerify(uint64_t, uint32_t, bool); uint64_t ValidateBlockAddress(access_mode) const; @@ -176,6 +180,8 @@ class Disk : public StorageDevice, private ScsiBlockCommands uint64_t sector_read_count = 0; uint64_t sector_write_count = 0; + string last_filename; + inline static const string SECTOR_READ_COUNT = "sector_read_count"; inline static const string SECTOR_WRITE_COUNT = "sector_write_count"; }; diff --git a/cpp/devices/disk_cache.cpp b/cpp/devices/disk_cache.cpp index 7bb4ee73..1ba44616 100644 --- a/cpp/devices/disk_cache.cpp +++ b/cpp/devices/disk_cache.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) // Copyright (C) 2014-2020 GIMONS @@ -30,7 +30,7 @@ bool DiskCache::Init() bool DiskCache::Flush() { // Save valid tracks - return ranges::none_of(cache.begin(), cache.end(), [this](const cache_t &c) + return ranges::none_of(cache.cbegin(), cache.cend(), [this](const cache_t &c) { return c.disktrk && !c.disktrk->Save(sec_path, cache_miss_write_count);}); } diff --git a/cpp/devices/disk_cache.h b/cpp/devices/disk_cache.h index f95d2786..0bc1fc8b 100644 --- a/cpp/devices/disk_cache.h +++ b/cpp/devices/disk_cache.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) // Copyright (C) 2014-2020 GIMONS @@ -21,7 +21,7 @@ class DiskCache : public Cache { // Number of tracks to cache - static const int CACHE_MAX = 16; + static constexpr int CACHE_MAX = 16; public: diff --git a/cpp/devices/disk_track.cpp b/cpp/devices/disk_track.cpp index dfa547bf..2658e4ff 100644 --- a/cpp/devices/disk_track.cpp +++ b/cpp/devices/disk_track.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) // Copyright (C) 2014-2020 GIMONS diff --git a/cpp/devices/disk_track.h b/cpp/devices/disk_track.h index 1ac72471..ef665c85 100644 --- a/cpp/devices/disk_track.h +++ b/cpp/devices/disk_track.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) // Copyright (C) 2014-2020 GIMONS diff --git a/cpp/devices/host_services.cpp b/cpp/devices/host_services.cpp index 633ad510..83f394e2 100644 --- a/cpp/devices/host_services.cpp +++ b/cpp/devices/host_services.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -86,7 +86,7 @@ #include #include "shared/shared_exceptions.h" #include "protobuf/protobuf_util.h" -#include "controllers/scsi_controller.h" +#include "controllers/controller.h" #include "base/memory_util.h" #include "host_services.h" @@ -96,7 +96,7 @@ using namespace google::protobuf::util; using namespace memory_util; using namespace protobuf_util; -HostServices::HostServices(int lun) : ModePageDevice(SCHS, scsi_level::spc_3, lun, false) +HostServices::HostServices(int lun) : ModePageDevice(SCHS, scsi_level::spc_3, lun, false, false) { SetProduct("Host Services"); } @@ -140,16 +140,11 @@ vector HostServices::InquiryInternal() const void HostServices::StartStopUnit() const { - const bool start = GetController()->GetCdbByte(4) & 0x01; const bool load = GetController()->GetCdbByte(4) & 0x02; - if (!start) { - if (load) { - GetController()->ScheduleShutdown(AbstractController::shutdown_mode::stop_pi); - } - else { - GetController()->ScheduleShutdown(AbstractController::shutdown_mode::stop_s2p); - } + if (const bool start = GetController()->GetCdbByte(4) & 0x01; !start) { + GetController()->ScheduleShutdown( + load ? AbstractController::shutdown_mode::stop_pi : AbstractController::shutdown_mode::stop_s2p); } else if (load) { GetController()->ScheduleShutdown(AbstractController::shutdown_mode::restart_pi); @@ -236,7 +231,7 @@ int HostServices::ModeSense6(cdb_t cdb, vector &buf) const const auto length = static_cast(min(buf.size(), static_cast(cdb[4]))); fill_n(buf.begin(), length, 0); - // 4 bytes basic information + // Basic information const int size = AddModePages(cdb, buf, 4, length, 255); // The size field does not count itself @@ -255,7 +250,7 @@ int HostServices::ModeSense10(cdb_t cdb, vector &buf) const const auto length = static_cast(min(buf.size(), static_cast(GetInt16(cdb, 7)))); fill_n(buf.begin(), length, 0); - // 8 bytes basic information + // Basic information const int size = AddModePages(cdb, buf, 8, length, 65535); // The size fields do not count themselves diff --git a/cpp/devices/host_services.h b/cpp/devices/host_services.h index 49dc687a..bbcfeb4a 100644 --- a/cpp/devices/host_services.h +++ b/cpp/devices/host_services.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -16,7 +16,6 @@ class HostServices : public ModePageDevice { - inline static const int EXECUTE_BUFFER_SIZE = 65535; public: @@ -80,4 +79,6 @@ class HostServices : public ModePageDevice S2pImage s2p_image; protobuf_format input_format = protobuf_format::binary; + + static constexpr int EXECUTE_BUFFER_SIZE = 65535; }; diff --git a/cpp/devices/linux_cache.cpp b/cpp/devices/linux_cache.cpp index 267b6506..5acf2eeb 100644 --- a/cpp/devices/linux_cache.cpp +++ b/cpp/devices/linux_cache.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2024 Uwe Seimet // diff --git a/cpp/devices/linux_cache.h b/cpp/devices/linux_cache.h index a98db084..763c6b9e 100644 --- a/cpp/devices/linux_cache.h +++ b/cpp/devices/linux_cache.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2024 Uwe Seimet // diff --git a/cpp/devices/mode_page_device.cpp b/cpp/devices/mode_page_device.cpp index b20cb983..ddb51ad5 100644 --- a/cpp/devices/mode_page_device.cpp +++ b/cpp/devices/mode_page_device.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -28,16 +28,15 @@ bool ModePageDevice::Init(const param_map ¶ms) ModeSense10(); }); - if (supports_mode_select) { - AddCommand(scsi_command::cmd_mode_select6, [this] - { - ModeSelect6(); - }); - AddCommand(scsi_command::cmd_mode_select10, [this] - { - ModeSelect10(); - }); - } + // Devices that implement MODE SENSE must also implement MODE SELECT + AddCommand(scsi_command::cmd_mode_select6, [this] + { + ModeSelect6(); + }); + AddCommand(scsi_command::cmd_mode_select10, [this] + { + ModeSelect10(); + }); return true; } @@ -53,7 +52,7 @@ int ModePageDevice::AddModePages(cdb_t cdb, vector &buf, int offset, in const int page_code = cdb[2] & 0x3f; - // Mode page data mapped to the respective page numbers, C++ maps are ordered by key + // Mode page data mapped to the respective page codes, C++ maps are ordered by key map> pages; SetUpModePages(pages, page_code, changeable); for (const auto& [p, data] : property_handler.GetCustomModePages(GetVendor(), GetProduct())) { @@ -72,32 +71,24 @@ int ModePageDevice::AddModePages(cdb_t cdb, vector &buf, int offset, in // Holds all mode page data vector result; - vector page0; - for (const auto& [index, data] : pages) { - // The specification mandates that page 0 must be returned after all others + for (const auto& [index, page_data] : pages) { + // The specification mandates that page 0 must be returned last if (index) { - const size_t off = result.size(); + const auto off = result.size(); // Page data - result.insert(result.end(), data.begin(), data.end()); + result.insert(result.end(), page_data.cbegin(), page_data.cend()); // Page code, PS bit may already have been set result[off] |= (byte)index; - // Page payload size - result[off + 1] = (byte)(data.size() - 2); - } - else { - page0 = data; + // Page payload size, does not count itself and the page code field + result[off + 1] = (byte)(page_data.size() - 2); } } - // Page 0 must be last - if (!page0.empty()) { - const size_t off = result.size(); - - // Page data - result.insert(result.end(), page0.begin(), page0.end()); - // Page payload size - result[off + 1] = (byte)(page0.size() - 2); + if (pages.contains(0)) { + // Page data only (there is no size field for page 0) + const auto &page_data = pages[0]; + result.insert(result.end(), page_data.cbegin(), page_data.cend()); } if (static_cast(result.size()) > max_size) { @@ -126,22 +117,30 @@ void ModePageDevice::ModeSelect(scsi_command, cdb_t, span, int) // There is no default implementation of MODE SELECT assert(false); - throw scsi_exception(sense_key::illegal_request, asc::invalid_command_operation_code); + throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb); } void ModePageDevice::ModeSelect6() const { + if (!supports_mode_select) { + throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb); + } + SaveParametersCheck(GetController()->GetCdbByte(4)); } void ModePageDevice::ModeSelect10() const { + if (!supports_mode_select) { + throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb); + } + SaveParametersCheck(GetInt16(GetController()->GetCdb(), 7)); } void ModePageDevice::SaveParametersCheck(int length) const { - if (!SupportsSaveParameters() && (GetController()->GetCdbByte(1) & 0x01)) { + if (!supports_save_parameters && (GetController()->GetCdbByte(1) & 0x01)) { throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb); } diff --git a/cpp/devices/mode_page_device.h b/cpp/devices/mode_page_device.h index 175a07cb..f41e32d4 100644 --- a/cpp/devices/mode_page_device.h +++ b/cpp/devices/mode_page_device.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -15,8 +15,8 @@ class ModePageDevice : public PrimaryDevice { public: - ModePageDevice(PbDeviceType type, scsi_level level, int lun, bool m) - : PrimaryDevice(type, level, lun), supports_mode_select(m) + ModePageDevice(PbDeviceType type, scsi_level level, int lun, bool m, bool s) + : PrimaryDevice(type, level, lun), supports_mode_select(m), supports_save_parameters(s) { } ~ModePageDevice() override = default; @@ -27,14 +27,6 @@ class ModePageDevice : public PrimaryDevice protected: - bool SupportsSaveParameters() const - { - return supports_save_parameters; - } - void SupportsSaveParameters(bool b) - { - supports_save_parameters = b; - } int AddModePages(cdb_t, vector&, int, int, int) const; virtual void SetUpModePages(map>&, int, bool) const = 0; virtual void AddVendorPages(map>&, int, bool) const @@ -56,7 +48,7 @@ class ModePageDevice : public PrimaryDevice bool supports_mode_select; - bool supports_save_parameters = false; + bool supports_save_parameters; PropertyHandler &property_handler = PropertyHandler::Instance(); }; diff --git a/cpp/devices/optical_memory.cpp b/cpp/devices/optical_memory.cpp index 012d6696..e8722e68 100644 --- a/cpp/devices/optical_memory.cpp +++ b/cpp/devices/optical_memory.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) // Copyright (C) 2014-2020 GIMONS @@ -15,7 +15,7 @@ using namespace memory_util; -OpticalMemory::OpticalMemory(int lun) : Disk(SCMO, scsi_level::scsi_2, lun, true, { 512, 1024, 2048, 4096 }) +OpticalMemory::OpticalMemory(int lun) : Disk(SCMO, scsi_level::scsi_2, lun, true, true, { 512, 1024, 2048, 4096 }) { // 128 MB, 512 bytes per sector, 248826 sectors geometries[512 * 248826] = { 512, 248826 }; @@ -30,7 +30,6 @@ OpticalMemory::OpticalMemory(int lun) : Disk(SCMO, scsi_level::scsi_2, lun, true SetProtectable(true); SetRemovable(true); SetLockable(true); - SupportsSaveParameters(true); } void OpticalMemory::Open() @@ -48,10 +47,6 @@ void OpticalMemory::Open() Disk::ValidateFile(); - if (!SetUpCache()) { - throw io_exception("Can't initialize cache"); - } - if (IsReady()) { SetAttn(true); } diff --git a/cpp/devices/optical_memory.h b/cpp/devices/optical_memory.h index 7398dc2f..e081b489 100644 --- a/cpp/devices/optical_memory.h +++ b/cpp/devices/optical_memory.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) // Copyright (C) 2014-2020 GIMONS diff --git a/cpp/devices/printer.cpp b/cpp/devices/printer.cpp index 13fcd25b..ff9cc59d 100644 --- a/cpp/devices/printer.cpp +++ b/cpp/devices/printer.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -65,20 +65,6 @@ bool Printer::Init(const param_map ¶ms) TestUnitReady(); }); - // Required also in this class in order to fulfill the ScsiPrinterCommands interface contract - AddCommand(scsi_command::cmd_reserve6, [this] - { - ReserveUnit(); - }); - AddCommand(scsi_command::cmd_release6, [this] - { - ReleaseUnit(); - }); - AddCommand(scsi_command::cmd_send_diagnostic, [this] - { - SendDiagnostic(); - }); - if (GetParam("cmd").find("%f") == string::npos) { LogTrace("Missing filename specifier %f"); return false; @@ -95,8 +81,6 @@ bool Printer::Init(const param_map ¶ms) void Printer::CleanUp() { - PrimaryDevice::CleanUp(); - if (out.is_open()) { out.close(); } diff --git a/cpp/devices/printer.h b/cpp/devices/printer.h index 8875f15e..8bd96cdb 100644 --- a/cpp/devices/printer.h +++ b/cpp/devices/printer.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -11,7 +11,6 @@ #include #include -#include #include "base/interfaces/scsi_printer_commands.h" #include "base/primary_device.h" @@ -19,19 +18,6 @@ using namespace std; class Printer : public PrimaryDevice, private ScsiPrinterCommands { - uint64_t file_print_count = 0; - uint64_t byte_receive_count = 0; - uint64_t print_error_count = 0; - uint64_t print_warning_count = 0; - - static const int NOT_RESERVED = -2; - - static constexpr const char *PRINTER_FILE_PATTERN = "/scsi2pi_sclp-XXXXXX"; - - inline static const string FILE_PRINT_COUNT = "file_print_count"; - inline static const string BYTE_RECEIVE_COUNT = "byte_receive_count"; - inline static const string PRINT_ERROR_COUNT = "print_error_count"; - inline static const string PRINT_WARNING_COUNT = "print_warning_count"; public: @@ -52,18 +38,6 @@ class Printer : public PrimaryDevice, private ScsiPrinterCommands private: void TestUnitReady() override; - void ReserveUnit() override - { - PrimaryDevice::ReserveUnit(); - } - void ReleaseUnit() override - { - PrimaryDevice::ReleaseUnit(); - } - void SendDiagnostic() override - { - PrimaryDevice::SendDiagnostic(); - } void Print() override; void SynchronizeBuffer(); @@ -72,4 +46,18 @@ class Printer : public PrimaryDevice, private ScsiPrinterCommands string filename; ofstream out; + + uint64_t file_print_count = 0; + uint64_t byte_receive_count = 0; + uint64_t print_error_count = 0; + uint64_t print_warning_count = 0; + + static constexpr int NOT_RESERVED = -2; + + static constexpr const char *PRINTER_FILE_PATTERN = "/scsi2pi_sclp-XXXXXX"; + + inline static const string FILE_PRINT_COUNT = "file_print_count"; + inline static const string BYTE_RECEIVE_COUNT = "byte_receive_count"; + inline static const string PRINT_ERROR_COUNT = "print_error_count"; + inline static const string PRINT_WARNING_COUNT = "print_warning_count"; }; diff --git a/cpp/devices/sasi_hd.cpp b/cpp/devices/sasi_hd.cpp index 3f53ad6b..4f082fbe 100644 --- a/cpp/devices/sasi_hd.cpp +++ b/cpp/devices/sasi_hd.cpp @@ -1,29 +1,21 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2023-2024 Uwe Seimet // //--------------------------------------------------------------------------- +#include "shared/shared_exceptions.h" #include "sasi_hd.h" -SasiHd::SasiHd(int lun, const unordered_set §or_sizes) : Disk(SAHD, scsi_level::none, lun, false, +SasiHd::SasiHd(int lun, const unordered_set §or_sizes) : Disk(SAHD, scsi_level::none, lun, false, false, sector_sizes) { SetProduct("SASI HD"); SetProtectable(true); } -void SasiHd::FinalizeSetup() -{ - Disk::ValidateFile(); - - if (!SetUpCache()) { - throw io_exception("Can't initialize cache"); - } -} - void SasiHd::Open() { assert(!IsReady()); @@ -34,14 +26,14 @@ void SasiHd::Open() } SetBlockCount(static_cast(GetFileSize() / GetSectorSizeInBytes())); - FinalizeSetup(); + Disk::ValidateFile(); } void SasiHd::Inquiry() { // Byte 0 = 0: Direct access device - array buf = { }; + const array buf = { }; GetController()->CopyToBuffer(buf.data(), buf.size()); DataInPhase(buf.size()); @@ -59,7 +51,7 @@ void SasiHd::RequestSense() //vector buf(allocation_length ? allocation_length : 4); // SASI fixed to non-extended format - array buf = { static_cast(GetSenseKey()), static_cast(GetLun() << 5) }; + const array buf = { static_cast(GetSenseKey()), static_cast(GetLun() << 5) }; GetController()->CopyToBuffer(buf.data(), buf.size()); DataInPhase(buf.size()); diff --git a/cpp/devices/sasi_hd.h b/cpp/devices/sasi_hd.h index d340ee44..3070fd0c 100644 --- a/cpp/devices/sasi_hd.h +++ b/cpp/devices/sasi_hd.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2023-2024 Uwe Seimet // @@ -9,7 +9,6 @@ #pragma once #include -#include #include "disk.h" class SasiHd : public Disk @@ -20,7 +19,6 @@ class SasiHd : public Disk explicit SasiHd(int, const unordered_set& = { 256, 512, 1024 }); ~SasiHd() override = default; - void FinalizeSetup(); void Open() override; void Inquiry() override; diff --git a/cpp/devices/scsi_cd.cpp b/cpp/devices/scsi_cd.cpp index 367c5759..2f4ebd49 100644 --- a/cpp/devices/scsi_cd.cpp +++ b/cpp/devices/scsi_cd.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp) // Copyright (C) 2014-2020 GIMONS @@ -14,8 +14,8 @@ using namespace memory_util; -ScsiCd::ScsiCd(int lun, bool scsi1) : Disk(SCCD, scsi1 ? scsi_level::scsi_1_ccs : scsi_level::scsi_2, lun, true, { 512, - 2048 }) +ScsiCd::ScsiCd(int lun, bool scsi1) : Disk(SCCD, scsi1 ? scsi_level::scsi_1_ccs : scsi_level::scsi_2, lun, true, false, + { 512, 2048 }) { SetProduct("SCSI CD-ROM"); SetReadOnly(true); @@ -45,15 +45,10 @@ void ScsiCd::Open() if (!SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 2048)) { throw io_exception("Invalid sector size"); } - SetBlockCount(GetFileSize() / GetSectorSizeInBytes()); Disk::ValidateFile(); - if (!SetUpCache()) { - throw io_exception("Can't initialize cache"); - } - SetReadOnly(true); SetProtectable(false); diff --git a/cpp/devices/scsi_cd.h b/cpp/devices/scsi_cd.h index 2a4272de..3d89b203 100644 --- a/cpp/devices/scsi_cd.h +++ b/cpp/devices/scsi_cd.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // diff --git a/cpp/devices/scsi_hd.cpp b/cpp/devices/scsi_hd.cpp index 8c9363c4..ad7a1bcf 100644 --- a/cpp/devices/scsi_hd.cpp +++ b/cpp/devices/scsi_hd.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -13,7 +13,7 @@ using namespace memory_util; ScsiHd::ScsiHd(int lun, bool removable, bool apple, bool scsi1, const unordered_set §or_sizes) -: Disk(removable ? SCRM : SCHD, scsi1 ? scsi_level::scsi_1_ccs : scsi_level::scsi_2, lun, true, sector_sizes) +: Disk(removable ? SCRM : SCHD, scsi1 ? scsi_level::scsi_1_ccs : scsi_level::scsi_2, lun, true, true, sector_sizes) { // Some Apple tools require a particular drive identification. // Except for the vendor string .hda is the same as .hds. @@ -26,7 +26,6 @@ ScsiHd::ScsiHd(int lun, bool removable, bool apple, bool scsi1, const unordered_ SetProtectable(true); SetRemovable(removable); SetLockable(removable); - SupportsSaveParameters(true); } string ScsiHd::GetProductData() const @@ -60,23 +59,17 @@ void ScsiHd::FinalizeSetup() if (!IsRemovable()) { SetProduct(GetProductData(), false); } - - if (!SetUpCache()) { - throw io_exception("Can't initialize cache"); - } } void ScsiHd::Open() { assert(!IsReady()); - const off_t size = GetFileSize(); - // Sector size (default 512 bytes) and number of blocks if (!SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 512)) { throw io_exception("Invalid sector size"); } - SetBlockCount(static_cast(size / GetSectorSizeInBytes())); + SetBlockCount(static_cast(GetFileSize() / GetSectorSizeInBytes())); FinalizeSetup(); } @@ -95,7 +88,7 @@ void ScsiHd::SetUpModePages(map> &pages, int page, bool change AddFormatPage(pages, changeable); } - // Page 4 (rigid drive page) + // Page 4 (rigid drive) if (page == 0x04 || page == 0x3f) { AddDrivePage(pages, changeable); } @@ -111,9 +104,9 @@ void ScsiHd::AddFormatPage(map> &pages, bool changeable) const vector buf(24); if (changeable) { - // The sector size is simulated to be changeable in multiples of 4, - // see the MODE SELECT implementation for details - SetInt16(buf, 12, 0xffff); + // The sector size is simulated to be changeable in multiples of 4 with a maximum of 4096 bytes per sector. + // See the MODE SELECT implementation for details. + SetInt16(buf, 12, 0x1ffc); pages[3] = buf; diff --git a/cpp/devices/scsi_hd.h b/cpp/devices/scsi_hd.h index 0414b5eb..077236bc 100644 --- a/cpp/devices/scsi_hd.h +++ b/cpp/devices/scsi_hd.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2023-2024 Uwe Seimet // diff --git a/cpp/devices/storage_device.cpp b/cpp/devices/storage_device.cpp index 9de13ce8..54fefc33 100644 --- a/cpp/devices/storage_device.cpp +++ b/cpp/devices/storage_device.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -12,8 +12,9 @@ using namespace filesystem; -StorageDevice::StorageDevice(PbDeviceType type, scsi_level level, int lun, bool supports_mode_pages) -: ModePageDevice(type, level, lun, supports_mode_pages) +StorageDevice::StorageDevice(PbDeviceType type, scsi_level level, int lun, bool supports_mode_select, + bool supports_save_parameters) +: ModePageDevice(type, level, lun, supports_mode_select, supports_save_parameters) { SupportsFile(true); SetStoppable(true); @@ -26,36 +27,17 @@ void StorageDevice::CleanUp() ModePageDevice::CleanUp(); } -void StorageDevice::SetFilename(string_view f) -{ - filename = filesystem::path(f); - - // Permanently write-protected - SetReadOnly(IsReadOnlyFile()); - - SetProtectable(!IsReadOnlyFile()); - - if (IsReadOnlyFile()) { - SetProtected(false); - } -} - void StorageDevice::ValidateFile() { if (!blocks) { - throw io_exception(string(GetTypeString()) + " device has 0 blocks"); - } - - if (!exists(filename)) { - throw file_not_found_exception( - "Image file '" + filename.string() + "' for " + GetTypeString() + " device does not exist"); + throw io_exception(GetTypeString() + " device has 0 blocks"); } if (GetFileSize() > 2LL * 1024 * 1024 * 1024 * 1024) { throw io_exception("Image files > 2 TiB are not supported"); } - // TODO Check for duplicate handling of these properties (-> S2pExecutor) + // TODO Check for duplicate handling of these properties (-> CommandExecutor) if (IsReadOnlyFile()) { // Permanently write-protected SetReadOnly(true); @@ -69,12 +51,15 @@ void StorageDevice::ValidateFile() SetReady(true); } -void StorageDevice::ReserveFile() const +bool StorageDevice::ReserveFile() const { - assert(!filename.empty()); - assert(!reserved_files.contains(filename.string())); + if (filename.empty() || reserved_files.contains(filename.string())) { + return false; + } reserved_files[filename.string()] = { GetId(), GetLun() }; + + return true; } void StorageDevice::UnreserveFile() diff --git a/cpp/devices/storage_device.h b/cpp/devices/storage_device.h index fd3bca46..98660c22 100644 --- a/cpp/devices/storage_device.h +++ b/cpp/devices/storage_device.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -20,7 +20,7 @@ class StorageDevice : public ModePageDevice { public: - StorageDevice(PbDeviceType, scsi_level, int, bool); + StorageDevice(PbDeviceType, scsi_level, int, bool, bool); ~StorageDevice() override = default; void CleanUp() override; @@ -31,14 +31,17 @@ class StorageDevice : public ModePageDevice { return filename.string(); } - void SetFilename(string_view); + void SetFilename(string_view file) + { + filename = filesystem::path(file); + } uint64_t GetBlockCount() const { return blocks; } - void ReserveFile() const; + bool ReserveFile() const; void UnreserveFile(); static bool FileExists(string_view); @@ -60,7 +63,7 @@ class StorageDevice : public ModePageDevice protected: - void ValidateFile(); + virtual void ValidateFile(); bool IsMediumChanged() const { diff --git a/cpp/devices/tap_driver.cpp b/cpp/devices/tap_driver.cpp index 1425fb31..e8b76495 100644 --- a/cpp/devices/tap_driver.cpp +++ b/cpp/devices/tap_driver.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2016-2020 GIMONS // Copyright (C) akuker @@ -57,6 +57,8 @@ bool TapDriver::Init(const param_map &const_params) return false; #else + const bool create_bridge = params["bridge"] == "true"; + inet = params["inet"]; trace("Setting up TAP interface " + BRIDGE_INTERFACE_NAME); @@ -73,7 +75,7 @@ bool TapDriver::Init(const param_map &const_params) const int ip_fd = socket(PF_INET, SOCK_DGRAM, 0); if (ip_fd == -1) { - error("Can't open IP socket: {}", strerror(errno)); + error("Can't create IP socket: {}", strerror(errno)); close(tap_fd); return false; } @@ -85,24 +87,24 @@ bool TapDriver::Init(const param_map &const_params) return false; }; - if (const string error = IpLink(true); !error.empty()) { + if (const string &error = IpLink(true); !error.empty()) { return cleanUp(error); } - // Only physical interfaces need a bridge - if (bridge_interface.starts_with("eth")) { + // Only physical interfaces need a bridge or can be in promiscuous mode + if (const bool is_physical = bridge_interface.starts_with("eth"); is_physical && create_bridge) { const int fd = socket(AF_LOCAL, SOCK_STREAM, 0); if (fd == -1) { - return cleanUp(fmt::format("Can't open bridge socket: {}", strerror(errno))); + return cleanUp(fmt::format("Can't create bridge socket: {}", strerror(errno))); } - if (const string error = CreateBridge(fd, ip_fd); !error.empty()) { + if (const string &error = CreateBridge(fd, ip_fd); !error.empty()) { close(fd); return cleanUp(error); } trace(">brctl addif " + BRIDGE_NAME + " " + BRIDGE_INTERFACE_NAME); - const string error = BrSetif(fd, BRIDGE_NAME, BRIDGE_INTERFACE_NAME, true); + const string &error = BrSetIf(fd, BRIDGE_INTERFACE_NAME, true); close(fd); if (!error.empty()) { return cleanUp(error); @@ -110,14 +112,14 @@ bool TapDriver::Init(const param_map &const_params) } else { trace(">ip addr add {0} brd + dev {1}", inet, BRIDGE_INTERFACE_NAME); - if (const string error = SetAddressAndNetMask(ip_fd, BRIDGE_INTERFACE_NAME); !error.empty()) { + if (const string &error = SetAddressAndNetMask(ip_fd, BRIDGE_INTERFACE_NAME); !error.empty()) { return cleanUp(error); } } close(ip_fd); - info("TAP interface " + BRIDGE_INTERFACE_NAME + " created"); + info("Created TAP interface " + BRIDGE_INTERFACE_NAME); return true; #endif @@ -131,20 +133,20 @@ void TapDriver::CleanUp() const if (bridge_created) { if (const int fd = socket(AF_LOCAL, SOCK_STREAM, 0); fd == -1) { - error("Can't open bridge socket: {}", strerror(errno)); + error("Can't create bridge socket: {}", strerror(errno)); } else { trace(">brctl delif " + BRIDGE_NAME + " " + BRIDGE_INTERFACE_NAME); - if (const string error = BrSetif(fd, BRIDGE_NAME, BRIDGE_INTERFACE_NAME, false); !error.empty()) { + if (const string &error = BrSetIf(fd, BRIDGE_INTERFACE_NAME, false); !error.empty()) { warn("Removing {0} from {1} failed: {2}", BRIDGE_INTERFACE_NAME, BRIDGE_NAME, error); warn("You may need to manually remove the TAP device"); } trace(">ip link set dev " + BRIDGE_NAME + " down"); - if (const string error = IpLink(fd, BRIDGE_NAME, false); !error.empty()) { + if (const string &error = IpLink(fd, BRIDGE_NAME, false); !error.empty()) { warn(error); } - if (const string error = DeleteBridge(fd); !error.empty()) { + if (const string &error = DeleteBridge(fd); !error.empty()) { warn(error); } @@ -159,7 +161,8 @@ param_map TapDriver::GetDefaultParams() const { return { { "interface", Join(available_interfaces, ",")}, - { "inet", DEFAULT_IP} + { "inet", DEFAULT_IP}, + { "bridge", "true"} }; } @@ -174,7 +177,7 @@ string TapDriver::CreateBridge(int bridge_fd, int ip_fd) } trace(">ip link set dev " + BRIDGE_NAME + " up"); - if (const string error = IpLink(ip_fd, BRIDGE_NAME, true); !error.empty()) { + if (const string &error = IpLink(ip_fd, BRIDGE_NAME, true); !error.empty()) { return error; } @@ -186,7 +189,7 @@ string TapDriver::CreateBridge(int bridge_fd, int ip_fd) string TapDriver::SetAddressAndNetMask(int fd, const string &interface) const { - const auto [address, netmask] = ExtractAddressAndMask(inet); + const auto [address, netmask] = ExtractAddressAndMask(); if (address.empty() || netmask.empty()) { return "Error extracting inet address and netmask"; } @@ -216,11 +219,11 @@ string TapDriver::SetAddressAndNetMask(int fd, const string &interface) const return ""; } -pair TapDriver::ExtractAddressAndMask(const string &addr) +pair TapDriver::ExtractAddressAndMask() const { - string address = addr; + string address = inet; string netmask = DEFAULT_NETMASK; - if (const auto &components = Split(addr, '/', 2); components.size() == 2) { + if (const auto &components = Split(inet, '/', 2); components.size() == 2) { address = components[0]; int m; @@ -247,7 +250,7 @@ string TapDriver::AddBridge(int fd) const } trace(">brctl addif {0} {1}", BRIDGE_NAME, bridge_interface); - if (const string error = BrSetif(fd, BRIDGE_NAME, bridge_interface, true); !error.empty()) { + if (const string &error = BrSetIf(fd, bridge_interface, true); !error.empty()) { return error; } #endif @@ -261,7 +264,7 @@ string TapDriver::DeleteBridge(int fd) const if (bridge_created) { trace(">brctl delbr " + BRIDGE_NAME); if (ioctl(fd, SIOCBRDELBR, BRIDGE_NAME.c_str()) == -1) { - return "Removing " + BRIDGE_NAME + " failed: " + strerror(errno); + return "Removing bridge " + BRIDGE_NAME + " failed: " + strerror(errno); } } #endif @@ -272,8 +275,13 @@ string TapDriver::DeleteBridge(int fd) const string TapDriver::IpLink(bool up) { const int fd = socket(PF_INET, SOCK_DGRAM, 0); + if (fd == -1) { + return fmt::format("Can't create socket: {}", strerror(errno)); + } + trace(string(">ip link set " + BRIDGE_INTERFACE_NAME + " ") + (up ? "up" : "down")); const string result = IpLink(fd, BRIDGE_INTERFACE_NAME, up); + close(fd); return result; @@ -299,7 +307,7 @@ string TapDriver::IpLink(int fd, const string &interface, bool up) return ""; } -string TapDriver::BrSetif(int fd, const string &bridge, const string &interface, bool add) +string TapDriver::BrSetIf(int fd, const string &interface, bool add) { #ifdef __linux__ ifreq ifr; @@ -308,7 +316,7 @@ string TapDriver::BrSetif(int fd, const string &bridge, const string &interface, return fmt::format("Can't if_nametoindex {0}: {1}", interface, strerror(errno)); } - strncpy(ifr.ifr_name, bridge.c_str(), IFNAMSIZ - 1); // NOSONAR Using strncpy is safe + strncpy(ifr.ifr_name, BRIDGE_NAME.c_str(), IFNAMSIZ - 1); // NOSONAR Using strncpy is safe if (ioctl(fd, add ? SIOCBRADDIF : SIOCBRDELIF, &ifr) == -1) { return fmt::format("Can't ioctl {0}: {1}", add ? "SIOCBRADDIF" : "SIOCBRDELIF", strerror(errno)); } diff --git a/cpp/devices/tap_driver.h b/cpp/devices/tap_driver.h index 7a6e19ed..60597097 100644 --- a/cpp/devices/tap_driver.h +++ b/cpp/devices/tap_driver.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // for Raspberry Pi // // Copyright (C) 2016-2020 GIMONS @@ -15,10 +15,7 @@ #include "base/device.h" #ifndef ETH_FRAME_LEN -static const int ETH_FRAME_LEN = 1514; -#endif -#ifndef ETH_FCS_LEN -static const int ETH_FCS_LEN = 4; +static constexpr int ETH_FRAME_LEN = 1514; #endif using namespace std; @@ -49,18 +46,18 @@ class TapDriver return BRIDGE_NAME; } - string AddBridge(int) const; - string DeleteBridge(int) const; - // Enable/Disable the piscsi0 interface static string IpLink(bool); private: + string AddBridge(int) const; + string DeleteBridge(int) const; + static string IpLink(int, const string&, bool); - static string BrSetif(int fd, const string&, const string&, bool); + static string BrSetIf(int fd, const string&, bool); string CreateBridge(int, int); - static pair ExtractAddressAndMask(const string&); + pair ExtractAddressAndMask() const; string SetAddressAndNetMask(int, const string&) const; int tap_fd = -1; diff --git a/cpp/initiator/initiator_executor.cpp b/cpp/initiator/initiator_executor.cpp index d8a33205..4cd887b3 100644 --- a/cpp/initiator/initiator_executor.cpp +++ b/cpp/initiator/initiator_executor.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2023-2024 Uwe Seimet // @@ -28,8 +28,7 @@ int InitiatorExecutor::Execute(scsi_command cmd, span cdb, span(cmd), count); } - if (const string &command_name = BusFactory::Instance().GetCommandName(cmd); - !command_name.empty()) { + if (const string &command_name = BusFactory::Instance().GetCommandName(cmd); !command_name.empty()) { trace("Executing command {0} for device {1}:{2}", command_name, target_id, target_lun); } else { @@ -202,7 +201,7 @@ void InitiatorExecutor::Status() { array buf; - if (bus.ReceiveHandShake(buf.data(), 1) != static_cast(buf.size())) { + if (bus.ReceiveHandShake(buf.data(), static_cast(buf.size())) != static_cast(buf.size())) { error("STATUS phase failed"); } else { diff --git a/cpp/initiator/initiator_executor.h b/cpp/initiator/initiator_executor.h index 2136b444..079b504e 100644 --- a/cpp/initiator/initiator_executor.h +++ b/cpp/initiator/initiator_executor.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2023-2024 Uwe Seimet // @@ -10,11 +10,11 @@ #include #include +#include #include #include "buses/bus.h" using namespace std; -using namespace scsi_defs; class InitiatorExecutor { @@ -78,20 +78,9 @@ class InitiatorExecutor int next_message = 0x80; - // Timeout values see bus.h - - inline static const long BUS_SETTLE_DELAY_NS = 400; - inline static const timespec BUS_SETTLE_DELAY = { .tv_sec = 0, .tv_nsec = BUS_SETTLE_DELAY_NS }; - - inline static const long BUS_CLEAR_DELAY_NS = 800; - inline static const timespec BUS_CLEAR_DELAY = { .tv_sec = 0, .tv_nsec = BUS_CLEAR_DELAY_NS }; - - inline static const long BUS_FREE_DELAY_NS = 800; - inline static const timespec BUS_FREE_DELAY = { .tv_sec = 0, .tv_nsec = BUS_FREE_DELAY_NS }; - - inline static const long DESKEW_DELAY_NS = 45; - inline static const timespec DESKEW_DELAY = { .tv_sec = 0, .tv_nsec = DESKEW_DELAY_NS }; - - inline static const long ARBITRATION_DELAY_NS = 2'400; - inline static const timespec ARBITRATION_DELAY = { .tv_sec = 0, .tv_nsec = ARBITRATION_DELAY_NS }; + static constexpr timespec BUS_SETTLE_DELAY = { .tv_sec = 0, .tv_nsec = 400 }; + static constexpr timespec BUS_CLEAR_DELAY = { .tv_sec = 0, .tv_nsec = 800 }; + static constexpr timespec BUS_FREE_DELAY = { .tv_sec = 0, .tv_nsec = 800 }; + static constexpr timespec DESKEW_DELAY = { .tv_sec = 0, .tv_nsec = 45 }; + static constexpr timespec ARBITRATION_DELAY = { .tv_sec = 0, .tv_nsec = 2'400 }; }; diff --git a/cpp/initiator/initiator_util.cpp b/cpp/initiator/initiator_util.cpp index 08b58b72..d481ea86 100644 --- a/cpp/initiator/initiator_util.cpp +++ b/cpp/initiator/initiator_util.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2024 Uwe Seimet // @@ -17,8 +17,8 @@ using namespace s2p_util; void initiator_util::ResetBus(Bus &bus) { bus.SetRST(true); - // 100 us should be enough, the standard requires at least 25 us - const timespec ts = { .tv_sec = 0, .tv_nsec = 100'000 }; + // 50 us should be enough, the specification requires at least 25 us + constexpr timespec ts = { .tv_sec = 0, .tv_nsec = 50'000 }; nanosleep(&ts, nullptr); bus.Reset(); } diff --git a/cpp/initiator/initiator_util.h b/cpp/initiator/initiator_util.h index e01b9898..334e6719 100644 --- a/cpp/initiator/initiator_util.h +++ b/cpp/initiator/initiator_util.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2024 Uwe Seimet // diff --git a/cpp/protobuf/command_context.cpp b/cpp/protobuf/command_context.cpp index f769f9f6..d117884b 100644 --- a/cpp/protobuf/command_context.cpp +++ b/cpp/protobuf/command_context.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2024 Uwe Seimet // diff --git a/cpp/protobuf/command_context.h b/cpp/protobuf/command_context.h index 5d096a44..6a967f6b 100644 --- a/cpp/protobuf/command_context.h +++ b/cpp/protobuf/command_context.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2024 Uwe Seimet // diff --git a/cpp/protobuf/protobuf_util.cpp b/cpp/protobuf/protobuf_util.cpp index 498deec9..857cf54a 100644 --- a/cpp/protobuf/protobuf_util.cpp +++ b/cpp/protobuf/protobuf_util.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2024 Uwe Seimet // @@ -34,9 +34,7 @@ PbCachingMode protobuf_util::ParseCachingMode(const string &value) string v = value; ranges::replace(v, '-', '_'); - string m; - ranges::transform(v, back_inserter(m), ::toupper); - if (PbCachingMode mode; PbCachingMode_Parse(m, &mode)) { + if (PbCachingMode mode; PbCachingMode_Parse(ToUpper(v), &mode)) { return mode; } @@ -206,7 +204,7 @@ string protobuf_util::ListDevices(const vector &pb_devices) void protobuf_util::SerializeMessage(int fd, const google::protobuf::Message &message) { const string s = message.SerializeAsString(); - vector data(s.begin(), s.end()); + vector data(s.cbegin(), s.cend()); // Write the size of the protobuf data as a header if (array header = { static_cast(data.size()), static_cast(data.size() >> 8), diff --git a/cpp/protobuf/protobuf_util.h b/cpp/protobuf/protobuf_util.h index 9e896d46..733a65b8 100644 --- a/cpp/protobuf/protobuf_util.h +++ b/cpp/protobuf/protobuf_util.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2024 Uwe Seimet // @@ -17,7 +17,8 @@ using namespace s2p_interface; namespace protobuf_util { -static const char KEY_VALUE_SEPARATOR = '='; + +static constexpr char KEY_VALUE_SEPARATOR = '='; string GetParam(const auto &item, const string &key) { diff --git a/cpp/s2p/s2p.cpp b/cpp/s2p/s2p.cpp index f43786f3..1d252e55 100644 --- a/cpp/s2p/s2p.cpp +++ b/cpp/s2p/s2p.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // diff --git a/cpp/s2p/s2p_core.cpp b/cpp/s2p/s2p_core.cpp index bf76e51d..1eaef74a 100644 --- a/cpp/s2p/s2p_core.cpp +++ b/cpp/s2p/s2p_core.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2016-2020 GIMONS // Copyright (C) 2020-2023 Contributors to the PiSCSI project @@ -16,6 +16,7 @@ #include #include #include "shared/s2p_version.h" +#include "shared/shared_exceptions.h" #include "buses/bus_factory.h" #include "base/device_factory.h" #include "protobuf/protobuf_util.h" @@ -172,8 +173,8 @@ int S2p::Run(span args, bool in_process) PbServerInfo server_info; CommandResponse response; response.GetDevices(executor->GetAllDevices(), server_info, s2p_image.GetDefaultFolder()); - const vector &devices = { server_info.devices_info().devices().begin(), - server_info.devices_info().devices().end() }; + const vector &devices = { server_info.devices_info().devices().cbegin(), + server_info.devices_info().devices().cend() }; const string device_list = ListDevices(devices); LogDevices(device_list); cout << device_list << flush; @@ -198,8 +199,8 @@ int S2p::Run(span args, bool in_process) bool S2p::ParseProperties(const property_map &properties, int &port) { + const auto &property_files = properties.find(PropertyHandler::PROPERTY_FILES); try { - const auto &property_files = properties.find(PropertyHandler::PROPERTY_FILES); property_handler.Init(property_files != properties.end() ? property_files->second : "", properties); if (const string &log_level = property_handler.GetProperty(PropertyHandler::LOG_LEVEL); diff --git a/cpp/s2p/s2p_core.h b/cpp/s2p/s2p_core.h index 5d5f1a44..6654f346 100644 --- a/cpp/s2p/s2p_core.h +++ b/cpp/s2p/s2p_core.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // diff --git a/cpp/s2p/s2p_parser.cpp b/cpp/s2p/s2p_parser.cpp index 6488a84b..d56a9ea6 100644 --- a/cpp/s2p/s2p_parser.cpp +++ b/cpp/s2p/s2p_parser.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2024 Uwe Seimet // @@ -9,6 +9,7 @@ #include #include #include "controllers/controller_factory.h" +#include "shared/shared_exceptions.h" #include "s2p/s2p_parser.h" using namespace s2p_util; @@ -20,12 +21,10 @@ void S2pParser::Banner(bool usage) const } else { cout << "Usage: s2p options ... FILE\n" - << " --scsi-id/-i ID[:LUN] SCSI target device ID (0-7) and\n" - << " LUN (0-" << (ControllerFactory::GetScsiLunMax() - 1) - << "), default LUN is 0.\n" - << " --sasi-id/-h ID[:LUN] SASI target device ID (0-7) and\n" - << " LUN (0-" << (ControllerFactory::GetSasiLunMax() - 1) - << "), default LUN is 0.\n" + << " --scsi-id/-i ID[:LUN] SCSI target device ID (0-7) and LUN (0-7),\n" + << " default LUN is 0.\n" + << " --sasi-id/-h ID[:LUN] SASI target device ID (0-7) and LUN (0-1),\n" + << " default LUN is 0.\n" << " --type/-t TYPE Device type.\n" << " --scsi-level LEVEL Optional SCSI standard level (1-8),\n" << " default is device-specific and usually SCSI-2.\n" @@ -118,7 +117,7 @@ property_map S2pParser::ParseArguments(span initial_args, bool &has_sasi) optind = 1; int opt; - while ((opt = getopt_long(static_cast(args.size()), args.data(), "-h:-i:b:c:l:m:n:p:r:t:z:C:F:L:P:R:BH", + while ((opt = getopt_long(static_cast(args.size()), args.data(), "-h:-i:b:c:l:m:n:p:r:t:z:C:F:L:P:R:B", options.data(), nullptr)) != -1) { if (const auto &property = OPTIONS_TO_PROPERTIES.find(opt); property != OPTIONS_TO_PROPERTIES.end()) { properties[property->second] = optarg; diff --git a/cpp/s2p/s2p_parser.h b/cpp/s2p/s2p_parser.h index 746841a0..697d8eae 100644 --- a/cpp/s2p/s2p_parser.h +++ b/cpp/s2p/s2p_parser.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2024 Uwe Seimet // diff --git a/cpp/s2p/s2p_thread.cpp b/cpp/s2p/s2p_thread.cpp index 5b11f702..e146444a 100644 --- a/cpp/s2p/s2p_thread.cpp +++ b/cpp/s2p/s2p_thread.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // diff --git a/cpp/s2p/s2p_thread.h b/cpp/s2p/s2p_thread.h index 00c7a3c2..a6d798bd 100644 --- a/cpp/s2p/s2p_thread.h +++ b/cpp/s2p/s2p_thread.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // diff --git a/cpp/s2pctl/s2pctl.cpp b/cpp/s2pctl/s2pctl.cpp index 9b115a9d..d3b5eb1d 100644 --- a/cpp/s2pctl/s2pctl.cpp +++ b/cpp/s2pctl/s2pctl.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // diff --git a/cpp/s2pctl/s2pctl_commands.cpp b/cpp/s2pctl/s2pctl_commands.cpp index 6c13b249..7f14e3a1 100644 --- a/cpp/s2pctl/s2pctl_commands.cpp +++ b/cpp/s2pctl/s2pctl_commands.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2024 Uwe Seimet // @@ -308,8 +308,8 @@ bool S2pCtlCommands::CommandServerInfo() } if (server_info.has_devices_info() && server_info.devices_info().devices_size()) { - vector sorted_devices = { server_info.devices_info().devices().begin(), - server_info.devices_info().devices().end() }; + vector sorted_devices = { server_info.devices_info().devices().cbegin(), + server_info.devices_info().devices().cend() }; ranges::sort(sorted_devices, [](const auto &a, const auto &b) {return a.id() < b.id() || a.unit() < b.unit();}); cout << "Attached devices:\n"; diff --git a/cpp/s2pctl/s2pctl_commands.h b/cpp/s2pctl/s2pctl_commands.h index 5f7224e9..cb5917ed 100644 --- a/cpp/s2pctl/s2pctl_commands.h +++ b/cpp/s2pctl/s2pctl_commands.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2023 Uwe Seimet // diff --git a/cpp/s2pctl/s2pctl_core.h b/cpp/s2pctl/s2pctl_core.h index 2523b105..e237130b 100644 --- a/cpp/s2pctl/s2pctl_core.h +++ b/cpp/s2pctl/s2pctl_core.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // diff --git a/cpp/s2pctl/s2pctl_display.cpp b/cpp/s2pctl/s2pctl_display.cpp index 2a572c19..903b8643 100644 --- a/cpp/s2pctl/s2pctl_display.cpp +++ b/cpp/s2pctl/s2pctl_display.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2024 Uwe Seimet // @@ -21,7 +21,7 @@ string S2pCtlDisplay::DisplayDevicesInfo(const PbDevicesInfo &devices_info) cons { ostringstream s; - const vector devices(devices_info.devices().begin(), devices_info.devices().end()); + const vector devices(devices_info.devices().cbegin(), devices_info.devices().cend()); s << ListDevices(devices); @@ -180,7 +180,7 @@ string S2pCtlDisplay::DisplayReservedIdsInfo(const PbReservedIdsInfo &reserved_i ostringstream s; if (reserved_ids_info.ids_size()) { - const set sorted_ids(reserved_ids_info.ids().begin(), reserved_ids_info.ids().end()); + const set sorted_ids(reserved_ids_info.ids().cbegin(), reserved_ids_info.ids().cend()); s << "Reserved device IDs: " << Join(sorted_ids) << '\n'; } @@ -214,7 +214,7 @@ string S2pCtlDisplay::DisplayImageFilesInfo(const PbImageFilesInfo &image_files_ s << "Supported folder depth: " << image_files_info.depth() << '\n'; if (!image_files_info.image_files().empty()) { - vector image_files(image_files_info.image_files().begin(), image_files_info.image_files().end()); + vector image_files(image_files_info.image_files().cbegin(), image_files_info.image_files().cend()); ranges::sort(image_files, [](const auto &a, const auto &b) {return a.name() < b.name();}); s << "Available image files:\n"; @@ -232,8 +232,8 @@ string S2pCtlDisplay::DisplayNetworkInterfaces(const PbNetworkInterfacesInfo &ne { ostringstream s; - const set> sorted_interfaces(network_interfaces_info.name().begin(), - network_interfaces_info.name().end()); + const set> sorted_interfaces(network_interfaces_info.name().cbegin(), + network_interfaces_info.name().cend()); s << "Available (up) network interfaces: " << Join(sorted_interfaces) << '\n'; return s.str(); @@ -245,7 +245,7 @@ string S2pCtlDisplay::DisplayMappingInfo(const PbMappingInfo &mapping_info) cons s << "Supported image file extension to device type mappings:\n"; - for (const map> sorted_mappings(mapping_info.mapping().begin(), mapping_info.mapping().end()); + for (const map> sorted_mappings(mapping_info.mapping().cbegin(), mapping_info.mapping().cend()); const auto& [extension, type] : sorted_mappings) { s << " " << extension << "->" << PbDeviceType_Name(type) << '\n'; } @@ -261,7 +261,7 @@ string S2pCtlDisplay::DisplayStatisticsInfo(const PbStatisticsInfo &statistics_i // Sort by ascending ID, LUN and key and by descending category vector sorted_statistics = - { statistics_info.statistics().begin(), statistics_info.statistics().end() }; + { statistics_info.statistics().cbegin(), statistics_info.statistics().cend() }; ranges::sort(sorted_statistics, [](const PbStatistics &a, const PbStatistics &b) { if (a.category() > b.category()) return true; if (a.category() < b.category()) return false; @@ -291,8 +291,8 @@ string S2pCtlDisplay::DisplayOperationInfo(const PbOperationInfo &operation_info { ostringstream s; - const map> operations(operation_info.operations().begin(), - operation_info.operations().end()); + const map> operations(operation_info.operations().cbegin(), + operation_info.operations().cend()); // Copies result into a map sorted by operation name auto unknown_operation = make_unique(); @@ -332,8 +332,8 @@ string S2pCtlDisplay::DisplayPropertiesInfo(const PbPropertiesInfo &properties_i { ostringstream s; - const map> sorted_properties(properties_info.s2p_properties().begin(), - properties_info.s2p_properties().end()); + const map> sorted_properties(properties_info.s2p_properties().cbegin(), + properties_info.s2p_properties().cend()); s << "s2p properties:\n"; for (const auto& [key, value] : sorted_properties) { @@ -406,7 +406,7 @@ string S2pCtlDisplay::DisplaySectorSizes(const PbDeviceProperties &properties) c ostringstream s; if (properties.block_sizes_size()) { - const set sorted_sizes(properties.block_sizes().begin(), properties.block_sizes().end()); + const set sorted_sizes(properties.block_sizes().cbegin(), properties.block_sizes().cend()); s << "Configurable sector sizes in bytes: " << Join(sorted_sizes); } @@ -415,7 +415,7 @@ string S2pCtlDisplay::DisplaySectorSizes(const PbDeviceProperties &properties) c string S2pCtlDisplay::DisplayParameters(const PbOperationMetaData &meta_data) const { - vector sorted_parameters(meta_data.parameters().begin(), meta_data.parameters().end()); + vector sorted_parameters(meta_data.parameters().cbegin(), meta_data.parameters().cend()); ranges::sort(sorted_parameters, [](const auto &a, const auto &b) {return a.name() < b.name();}); ostringstream s; @@ -443,8 +443,8 @@ string S2pCtlDisplay::DisplayPermittedValues(const PbOperationParameter ¶met { ostringstream s; if (parameter.permitted_values_size()) { - const set> sorted_values(parameter.permitted_values().begin(), - parameter.permitted_values().end()); + const set> sorted_values(parameter.permitted_values().cbegin(), + parameter.permitted_values().cend()); s << " Permitted values: " << Join(sorted_values) << '\n'; } diff --git a/cpp/s2pctl/s2pctl_display.h b/cpp/s2pctl/s2pctl_display.h index d92fc039..a4ec7e18 100644 --- a/cpp/s2pctl/s2pctl_display.h +++ b/cpp/s2pctl/s2pctl_display.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2023 Uwe Seimet // diff --git a/cpp/s2pctl/sp2ctl_core.cpp b/cpp/s2pctl/sp2ctl_core.cpp index 1c09ba7f..0d73caaf 100644 --- a/cpp/s2pctl/sp2ctl_core.cpp +++ b/cpp/s2pctl/sp2ctl_core.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2024 Uwe Seimet // @@ -351,7 +351,7 @@ int S2pCtl::ParseArguments(const vector &args) // NOSONAR Acceptable comp device->set_caching_mode(ParseCachingMode(optarg)); } catch (const parser_exception &e) { - cerr << e.what() << endl; + cerr << "Error: " << e.what() << endl; return EXIT_FAILURE; } break; @@ -446,7 +446,7 @@ int S2pCtl::ParseArguments(const vector &args) // NOSONAR Acceptable comp S2pCtlCommands s2pctl_commands(command, hostname, port, filename_binary, filename_json, filename_text); - bool status; + bool status = false; try { // Listing devices is a special case if (command.operation() == DEVICES_INFO) { @@ -462,10 +462,7 @@ int S2pCtl::ParseArguments(const vector &args) // NOSONAR Acceptable comp } catch (const io_exception &e) { cerr << "Error: " << e.what() << endl; - - status = false; - - // Fall through + return EXIT_FAILURE; } return status ? EXIT_SUCCESS : EXIT_FAILURE; diff --git a/cpp/s2pdump/s2pdump.cpp b/cpp/s2pdump/s2pdump.cpp index 9ddb7181..716da67f 100644 --- a/cpp/s2pdump/s2pdump.cpp +++ b/cpp/s2pdump/s2pdump.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // diff --git a/cpp/s2pdump/s2pdump_core.cpp b/cpp/s2pdump/s2pdump_core.cpp index 1109dd2f..2ae313a5 100644 --- a/cpp/s2pdump/s2pdump_core.cpp +++ b/cpp/s2pdump/s2pdump_core.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -79,6 +79,13 @@ void S2pDump::Banner(bool header) const bool S2pDump::Init(bool in_process) { + bus = BusFactory::Instance().CreateBus(false, in_process); + if (!bus) { + return false; + } + + executor = make_unique(*bus, initiator_id); + instance = this; // Signal handler for cleaning up struct sigaction termination_handler; @@ -89,12 +96,7 @@ bool S2pDump::Init(bool in_process) sigaction(SIGTERM, &termination_handler, nullptr); signal(SIGPIPE, SIG_IGN); - bus = BusFactory::Instance().CreateBus(false, in_process); - if (bus) { - executor = make_unique(*bus, initiator_id); - } - - return bus != nullptr; + return true; } bool S2pDump::ParseArguments(span args) // NOSONAR Acceptable complexity for parsing @@ -304,10 +306,6 @@ int S2pDump::Run(span args, bool in_process) return EXIT_SUCCESS; } - if (!in_process && getuid()) { - throw parser_exception("GPIO bus access requires root permissions"); - } - if (!Init(in_process)) { throw parser_exception("Can't initialize bus"); } @@ -732,12 +730,13 @@ void S2pDump::DisplayProperties(int id, int lun) const // Mode page 0 has no length field, i.e. its length is the remaining number of bytes const int page_length = page_code ? buf[offset] : length - offset; - ++offset; cout << fmt::format("{0}mode_page.{1}={2:02x}", id_and_lun, page_code & 0x3f, page_code); if (page_code) { cout << fmt::format(":{:02x}", page_length); + + ++offset; } for (int i = 0; i < page_length && offset < length; i++, offset++) { diff --git a/cpp/s2pdump/s2pdump_core.h b/cpp/s2pdump/s2pdump_core.h index 62583d4c..61dd94b8 100644 --- a/cpp/s2pdump/s2pdump_core.h +++ b/cpp/s2pdump/s2pdump_core.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -90,8 +90,8 @@ class S2pDump // Required for the termination handler static inline S2pDump *instance; - static const int MINIMUM_BUFFER_SIZE = 1024 * 64; - static const int DEFAULT_BUFFER_SIZE = 1024 * 1024; + static constexpr int MINIMUM_BUFFER_SIZE = 1024 * 64; + static constexpr int DEFAULT_BUFFER_SIZE = 1024 * 1024; static inline const string DIVIDER = "----------------------------------------"; diff --git a/cpp/s2pdump/s2pdump_executor.cpp b/cpp/s2pdump/s2pdump_executor.cpp index 20557585..23d6332e 100644 --- a/cpp/s2pdump/s2pdump_executor.cpp +++ b/cpp/s2pdump/s2pdump_executor.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2023-2024 Uwe Seimet // diff --git a/cpp/s2pdump/s2pdump_executor.h b/cpp/s2pdump/s2pdump_executor.h index 9f6dea95..a10ea7ce 100644 --- a/cpp/s2pdump/s2pdump_executor.h +++ b/cpp/s2pdump/s2pdump_executor.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2023-2024 Uwe Seimet // diff --git a/cpp/s2pexec/s2pexec.cpp b/cpp/s2pexec/s2pexec.cpp index 986bf2a2..eebcff51 100644 --- a/cpp/s2pexec/s2pexec.cpp +++ b/cpp/s2pexec/s2pexec.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2023-2024 Uwe Seimet // diff --git a/cpp/s2pexec/s2pexec_core.cpp b/cpp/s2pexec/s2pexec_core.cpp index 57813bfe..c0e60f6c 100644 --- a/cpp/s2pexec/s2pexec_core.cpp +++ b/cpp/s2pexec/s2pexec_core.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2023-2024 Uwe Seimet // @@ -72,6 +72,13 @@ void S2pExec::Banner(bool header, bool usage) bool S2pExec::Init(bool in_process) { + bus = BusFactory::Instance().CreateBus(false, in_process); + if (!bus) { + return false; + } + + executor = make_unique(*bus, initiator_id); + instance = this; // Signal handler for cleaning up struct sigaction termination_handler; @@ -82,12 +89,7 @@ bool S2pExec::Init(bool in_process) sigaction(SIGTERM, &termination_handler, nullptr); signal(SIGPIPE, SIG_IGN); - bus = BusFactory::Instance().CreateBus(false, in_process); - if (bus) { - executor = make_unique(*bus, initiator_id); - } - - return bus != nullptr; + return true; } bool S2pExec::ParseArguments(span args) @@ -406,7 +408,7 @@ tuple S2pExec::ExecuteCommand() try { cmd_bytes = HexToBytes(command); } - catch (const parser_exception&) + catch (const out_of_range&) { throw execution_exception("Invalid CDB input format: '" + command + "'"); } @@ -461,7 +463,7 @@ string S2pExec::ReadData() const string &filename = binary_input_filename.empty() ? hex_input_filename : binary_input_filename; const bool text = binary_input_filename.empty(); - fstream in(filename, text ? ios::in : ios::in | ios::binary); + ifstream in(filename, text ? ios::in : ios::in | ios::binary); if (in.fail()) { return fmt::format("Can't open input file '{0}': {1}", filename, strerror(errno)); } @@ -497,7 +499,7 @@ string S2pExec::WriteData(int count) } } else { - fstream out(filename, text ? ios::out : ios::out | ios::binary); + ofstream out(filename, text ? ios::out : ios::out | ios::binary); if (out.fail()) { return fmt::format("Can't open output file '{0}': {1}", filename, strerror(errno)); } @@ -514,13 +516,13 @@ string S2pExec::WriteData(int count) return ""; } -string S2pExec::ConvertData(const string &data) +string S2pExec::ConvertData(const string &hex) { vector bytes; try { - bytes = HexToBytes(data); + bytes = HexToBytes(hex); } - catch (const parser_exception&) { + catch (const out_of_range&) { return "Invalid data input format"; } diff --git a/cpp/s2pexec/s2pexec_core.h b/cpp/s2pexec/s2pexec_core.h index 071c037a..93c42a2f 100644 --- a/cpp/s2pexec/s2pexec_core.h +++ b/cpp/s2pexec/s2pexec_core.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -81,5 +81,5 @@ class S2pExec // Required for the termination handler static inline S2pExec *instance; - inline static const int DEFAULT_BUFFER_SIZE = 131072; + static constexpr int DEFAULT_BUFFER_SIZE = 131072; }; diff --git a/cpp/s2pexec/s2pexec_executor.cpp b/cpp/s2pexec/s2pexec_executor.cpp index 41540a2a..31261092 100644 --- a/cpp/s2pexec/s2pexec_executor.cpp +++ b/cpp/s2pexec/s2pexec_executor.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2023-2024 Uwe Seimet // diff --git a/cpp/s2pexec/s2pexec_executor.h b/cpp/s2pexec/s2pexec_executor.h index 2e14eab3..204ae1fc 100644 --- a/cpp/s2pexec/s2pexec_executor.h +++ b/cpp/s2pexec/s2pexec_executor.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2023-2024 Uwe Seimet // @@ -16,8 +16,6 @@ using namespace std; class S2pExecExecutor { - // The SCSI ExecuteOperation custom command supports a byte count of up to 65535 bytes - inline static const int BUFFER_SIZE = 65535; public: @@ -53,4 +51,7 @@ class S2pExecExecutor private: unique_ptr initiator_executor; + + // The SCSI ExecuteOperation custom command supports a byte count of up to 65535 bytes + static constexpr int BUFFER_SIZE = 65535; }; diff --git a/cpp/s2pproto/s2pproto.cpp b/cpp/s2pproto/s2pproto.cpp index 0c75a2d7..40b44a40 100644 --- a/cpp/s2pproto/s2pproto.cpp +++ b/cpp/s2pproto/s2pproto.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2023-2024 Uwe Seimet // diff --git a/cpp/s2pproto/s2pproto_core.cpp b/cpp/s2pproto/s2pproto_core.cpp index a17d1653..963f243b 100644 --- a/cpp/s2pproto/s2pproto_core.cpp +++ b/cpp/s2pproto/s2pproto_core.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2023-2024 Uwe Seimet // @@ -70,6 +70,13 @@ void S2pProto::Banner(bool header) bool S2pProto::Init(bool in_process) { + bus = BusFactory::Instance().CreateBus(false, in_process); + if (!bus) { + return false; + } + + executor = make_unique(*bus, initiator_id); + instance = this; // Signal handler for cleaning up struct sigaction termination_handler; @@ -80,12 +87,7 @@ bool S2pProto::Init(bool in_process) sigaction(SIGTERM, &termination_handler, nullptr); signal(SIGPIPE, SIG_IGN); - bus = BusFactory::Instance().CreateBus(false, in_process); - if (bus) { - executor = make_unique(*bus, initiator_id); - } - - return bus != nullptr; + return true; } bool S2pProto::ParseArguments(span args) @@ -241,15 +243,14 @@ int S2pProto::Run(span args, bool in_process) executor->SetTarget(target_id, target_lun, false); - int result = GenerateOutput(input_format, protobuf_input_filename, output_format, protobuf_output_filename); + int result = GenerateOutput(protobuf_input_filename, protobuf_output_filename); CleanUp(); return result; } -int S2pProto::GenerateOutput(S2pProtoExecutor::protobuf_format input_format, const string &input_filename, - S2pProtoExecutor::protobuf_format output_format, const string &output_filename) +int S2pProto::GenerateOutput(const string &input_filename, const string &output_filename) { PbResult result; if (string error = executor->Execute(input_filename, input_format, result); !error.empty()) { diff --git a/cpp/s2pproto/s2pproto_core.h b/cpp/s2pproto/s2pproto_core.h index d71f1e7a..36418328 100644 --- a/cpp/s2pproto/s2pproto_core.h +++ b/cpp/s2pproto/s2pproto_core.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2023-2024 Uwe Seimet // @@ -27,8 +27,7 @@ class S2pProto bool Init(bool); bool ParseArguments(span); - int GenerateOutput(S2pProtoExecutor::protobuf_format, const string&, S2pProtoExecutor::protobuf_format, - const string&); + int GenerateOutput(const string&, const string&); void CleanUp() const; static void TerminationHandler(int); diff --git a/cpp/s2pproto/s2pproto_executor.cpp b/cpp/s2pproto/s2pproto_executor.cpp index be8ae93b..fdf90d3b 100644 --- a/cpp/s2pproto/s2pproto_executor.cpp +++ b/cpp/s2pproto/s2pproto_executor.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2023-2024 Uwe Seimet // @@ -45,7 +45,7 @@ string S2pProtoExecutor::Execute(const string &filename, protobuf_format input_f stringstream buf; buf << in.rdbuf(); - const string data = buf.str(); + const string &data = buf.str(); length = data.size(); memcpy(buffer.data(), data.data(), length); break; diff --git a/cpp/s2pproto/s2pproto_executor.h b/cpp/s2pproto/s2pproto_executor.h index f0a0f246..c886621e 100644 --- a/cpp/s2pproto/s2pproto_executor.h +++ b/cpp/s2pproto/s2pproto_executor.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2023-2024 Uwe Seimet // @@ -18,9 +18,6 @@ using namespace s2p_interface; class S2pProtoExecutor { - // The SCSI ExecuteOperation command supports a byte count of up to 65535 bytes - inline static const int BUFFER_SIZE = 65535; - public: enum class protobuf_format @@ -49,6 +46,9 @@ class S2pProtoExecutor private: + // The SCSI ExecuteOperation command supports a byte count of up to 65535 bytes + static constexpr int BUFFER_SIZE = 65535; + array buffer; unique_ptr initiator_executor; diff --git a/cpp/shared/localizer.cpp b/cpp/shared/localizer.cpp index cab541e5..eb6d02ac 100644 --- a/cpp/shared/localizer.cpp +++ b/cpp/shared/localizer.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2024 Uwe Seimet // @@ -215,6 +215,13 @@ Localizer::Localizer() Add(LocalizationKey::ERROR_INVALID_LUN, "es", "LUN invalido %1 (0-%2)"); Add(LocalizationKey::ERROR_INVALID_LUN, "zh", "无效的 LUN %1 (0-%2)"); + Add(LocalizationKey::ERROR_MISSING_LUN0, "en", "Missing LUN 0 for device ID %1"); + Add(LocalizationKey::ERROR_MISSING_LUN0, "de", "Fehlende LUN 0 für Geräte-ID %1"); + Add(LocalizationKey::ERROR_MISSING_LUN0, "sv", "Saknar LUN 0 för enhets-ID %1"); + Add(LocalizationKey::ERROR_MISSING_LUN0, "fr", "LUN 0 manquant pour l'ID de périphérique %1"); + Add(LocalizationKey::ERROR_MISSING_LUN0, "es", "Falta LUN 0 para la ID del dispositivo %1"); + Add(LocalizationKey::ERROR_MISSING_LUN0, "zh", "设备 ID %1 缺少 LUN 0"); + Add(LocalizationKey::ERROR_LUN0, "en", "LUN 0 cannot be detached as long as there is still another LUN"); Add(LocalizationKey::ERROR_LUN0, "de", "LUN 0 kann nicht entfernt werden, solange noch eine andere LUN existiert"); Add(LocalizationKey::ERROR_LUN0, "sv", diff --git a/cpp/shared/localizer.h b/cpp/shared/localizer.h index 7b7ad96b..f1065448 100644 --- a/cpp/shared/localizer.h +++ b/cpp/shared/localizer.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2024 Uwe Seimet // @@ -43,6 +43,7 @@ enum class LocalizationKey ERROR_CONTROLLER, ERROR_INVALID_ID, ERROR_INVALID_LUN, + ERROR_MISSING_LUN0, ERROR_LUN0, ERROR_INITIALIZATION, ERROR_OPERATION_DENIED_STOPPABLE, diff --git a/cpp/shared/network_util.cpp b/cpp/shared/network_util.cpp index d242de7d..930c41fd 100644 --- a/cpp/shared/network_util.cpp +++ b/cpp/shared/network_util.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2023-2024 Uwe Seimet // diff --git a/cpp/shared/network_util.h b/cpp/shared/network_util.h index c58fc4ed..c8232fb0 100644 --- a/cpp/shared/network_util.h +++ b/cpp/shared/network_util.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2023-2024 Uwe Seimet // diff --git a/cpp/shared/s2p_util.cpp b/cpp/shared/s2p_util.cpp index dcf9af89..0b1382b0 100644 --- a/cpp/shared/s2p_util.cpp +++ b/cpp/shared/s2p_util.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2024 Uwe Seimet // @@ -244,7 +244,7 @@ vector s2p_util::HexToBytes(const string &hex) string line; while (getline(ss, line)) { if (line.starts_with(":") || line.ends_with(":")) { - throw parser_exception(""); + throw out_of_range(""); } const string &line_lower = ToLower(line); @@ -255,13 +255,7 @@ vector s2p_util::HexToBytes(const string &hex) i++; } - try { - bytes.push_back( - static_cast((HEX_TO_DEC.at(line_lower[i]) << 4) + HEX_TO_DEC.at(line_lower[i + 1]))); - } - catch (const out_of_range&) { - throw parser_exception(""); - } + bytes.push_back(static_cast((HexToDec(line_lower[i]) << 4) + HexToDec(line_lower[i + 1]))); i += 2; } @@ -311,3 +305,16 @@ string s2p_util::FormatBytes(vector &bytes, int count, bool hex_only) return str; } + +int s2p_util::HexToDec(char c) +{ + if (c >= '0' && c <= '9') { + return c -'0'; + } + + if (c >= 'a' && c <= 'f') { + return c - 'a' + 10; + } + + throw out_of_range(""); +} diff --git a/cpp/shared/s2p_util.h b/cpp/shared/s2p_util.h index a5861154..c84a68cc 100644 --- a/cpp/shared/s2p_util.h +++ b/cpp/shared/s2p_util.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2024 Uwe Seimet // @@ -16,8 +16,9 @@ namespace s2p_util { + // Separator for compound options like ID:LUN -static const char COMPONENT_SEPARATOR = ':'; +static constexpr char COMPONENT_SEPARATOR = ':'; struct StringHash { @@ -65,9 +66,5 @@ string FormatSenseData(scsi_defs::sense_key, scsi_defs::asc, int = 0); vector HexToBytes(const string&); string FormatBytes(vector&, int, bool = false); - -const unordered_map HEX_TO_DEC = { - { '0', 0 }, { '1', 1 }, { '2', 2 }, { '3', 3 }, { '4', 4 }, { '5', 5 }, { '6', 6 }, { '7', 7 }, { '8', 8 }, - { '9', 9 }, { 'a', 10 }, { 'b', 11 }, { 'c', 12 }, { 'd', 13 }, { 'e', 14 }, { 'f', 15 } -}; +int HexToDec(char); } diff --git a/cpp/shared/s2p_version.cpp b/cpp/shared/s2p_version.cpp index a80c906e..e1fb28ec 100644 --- a/cpp/shared/s2p_version.cpp +++ b/cpp/shared/s2p_version.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2023-2024 Uwe Seimet // @@ -9,6 +9,6 @@ #include "s2p_version.h" const int s2p_major_version = 3; -const int s2p_minor_version = 0; -const int s2p_revision = 2; +const int s2p_minor_version = 1; +const int s2p_revision = 0; const std::string s2p_suffix = ""; diff --git a/cpp/shared/s2p_version.h b/cpp/shared/s2p_version.h index 33662262..eac348a4 100644 --- a/cpp/shared/s2p_version.h +++ b/cpp/shared/s2p_version.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2023 Uwe Seimet // diff --git a/cpp/shared/scsi.h b/cpp/shared/scsi.h index 266f4f25..f411a8e4 100644 --- a/cpp/shared/scsi.h +++ b/cpp/shared/scsi.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2024 Uwe Seimet // @@ -29,20 +29,19 @@ enum class scsi_level spc_6 = 8 }; -// Phase definitions enum class phase_t { - busfree, - arbitration, - selection, - reselection, - command, - datain, - dataout, - status, - msgin, - msgout, - reserved + busfree = 0, + arbitration = 1, + selection = 2, + reselection = 3, + command = 4, + datain = 5, + dataout = 6, + status = 7, + msgin = 8, + msgout = 9, + reserved = 10 }; enum class device_type diff --git a/cpp/shared/shared_exceptions.h b/cpp/shared/shared_exceptions.h index d59d8335..e18248be 100644 --- a/cpp/shared/shared_exceptions.h +++ b/cpp/shared/shared_exceptions.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2024 Uwe Seimet // diff --git a/cpp/test/abstract_controller_test.cpp b/cpp/test/abstract_controller_test.cpp index fbfef4f7..44e981e5 100644 --- a/cpp/test/abstract_controller_test.cpp +++ b/cpp/test/abstract_controller_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -15,13 +15,17 @@ TEST(AbstractControllerTest, ShutdownMode) { MockAbstractController controller; - EXPECT_EQ(AbstractController::shutdown_mode::none, controller.GetShutdownMode()); + EXPECT_CALL(controller, Process); + EXPECT_EQ(AbstractController::shutdown_mode::none, controller.ProcessOnController(0)); controller.ScheduleShutdown(AbstractController::shutdown_mode::stop_s2p); - EXPECT_EQ(AbstractController::shutdown_mode::stop_s2p, controller.GetShutdownMode()); + EXPECT_CALL(controller, Process); + EXPECT_EQ(AbstractController::shutdown_mode::stop_s2p, controller.ProcessOnController(0)); controller.ScheduleShutdown(AbstractController::shutdown_mode::stop_pi); - EXPECT_EQ(AbstractController::shutdown_mode::stop_pi, controller.GetShutdownMode()); + EXPECT_CALL(controller, Process); + EXPECT_EQ(AbstractController::shutdown_mode::stop_pi, controller.ProcessOnController(0)); controller.ScheduleShutdown(AbstractController::shutdown_mode::restart_pi); - EXPECT_EQ(AbstractController::shutdown_mode::restart_pi, controller.GetShutdownMode()); + EXPECT_CALL(controller, Process); + EXPECT_EQ(AbstractController::shutdown_mode::restart_pi, controller.ProcessOnController(0)); } TEST(AbstractControllerTest, SetCurrentLength) @@ -52,14 +56,6 @@ TEST(AbstractControllerTest, Reset) EXPECT_EQ(0, controller->GetCurrentLength()); } -TEST(AbstractControllerTest, Message) -{ - MockAbstractController controller; - - controller.SetMessage(0x12); - EXPECT_EQ(0x12, controller.GetMessage()); -} - TEST(AbstractControllerTest, Status) { MockAbstractController controller; @@ -73,13 +69,13 @@ TEST(AbstractControllerTest, DeviceLunLifeCycle) const int ID = 1; const int LUN = 4; - auto controller = make_shared>(ID); + auto controller = make_shared(ID); auto device1 = make_shared(LUN); auto device2 = make_shared(32); auto device3 = make_shared(-1); - EXPECT_EQ(0, controller->GetLunCount()); + EXPECT_EQ(0U, controller->GetLunCount()); EXPECT_EQ(ID, controller->GetTargetId()); EXPECT_TRUE(controller->AddDevice(device1)); EXPECT_FALSE(controller->AddDevice(device2)); @@ -88,7 +84,7 @@ TEST(AbstractControllerTest, DeviceLunLifeCycle) EXPECT_NE(nullptr, controller->GetDeviceForLun(LUN)); EXPECT_EQ(nullptr, controller->GetDeviceForLun(0)); EXPECT_TRUE(controller->RemoveDevice(*device1)); - EXPECT_EQ(0, controller->GetLunCount()); + EXPECT_EQ(0U, controller->GetLunCount()); EXPECT_FALSE(controller->RemoveDevice(*device1)); } @@ -100,21 +96,12 @@ TEST(AbstractControllerTest, GetOpcode) EXPECT_EQ(scsi_command::cmd_inquiry, controller.GetOpcode()); } -TEST(AbstractControllerTest, GetLun) -{ - const int LUN = 3; - - MockAbstractController controller; - - controller.SetCdbByte(1, LUN << 5); - EXPECT_EQ(LUN, controller.GetLun()); -} - TEST(AbstractControllerTest, TransferSize) { MockAbstractController controller; controller.SetTransferSize(3, 1); + EXPECT_EQ(1, controller.GetChunkSize()); EXPECT_TRUE(controller.UpdateTransferSize()); EXPECT_TRUE(controller.UpdateTransferSize()); EXPECT_FALSE(controller.UpdateTransferSize()); @@ -145,6 +132,8 @@ TEST(AbstractControllerTest, ProcessOnController) auto bus = make_shared(); auto controller = make_shared(bus, 1); - EXPECT_CALL(*controller, Process(-1)); + EXPECT_CALL(*controller, Process()); controller->ProcessOnController(0x02); + EXPECT_CALL(*controller, Process()); + controller->ProcessOnController(0x06); } diff --git a/cpp/test/bus_factory_test.cpp b/cpp/test/bus_factory_test.cpp index 9a088015..dbe761a2 100644 --- a/cpp/test/bus_factory_test.cpp +++ b/cpp/test/bus_factory_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2024 Uwe Seimet // diff --git a/cpp/test/bus_test.cpp b/cpp/test/bus_test.cpp index 92a91b74..239e2df1 100644 --- a/cpp/test/bus_test.cpp +++ b/cpp/test/bus_test.cpp @@ -1,67 +1,13 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // //--------------------------------------------------------------------------- -#include "mocks.h" - -TEST(BusTest, GetPhase) -{ - EXPECT_EQ(phase_t::dataout, Bus::GetPhase(0b000)); - EXPECT_EQ(phase_t::datain, Bus::GetPhase(0b001)); - EXPECT_EQ(phase_t::command, Bus::GetPhase(0b010)); - EXPECT_EQ(phase_t::status, Bus::GetPhase(0b011)); - EXPECT_EQ(phase_t::reserved, Bus::GetPhase(0b100)); - EXPECT_EQ(phase_t::reserved, Bus::GetPhase(0b101)); - EXPECT_EQ(phase_t::msgout, Bus::GetPhase(0b110)); - EXPECT_EQ(phase_t::msgin, Bus::GetPhase(0b111)); - - NiceMock bus; - - EXPECT_EQ(phase_t::busfree, bus.GetPhase()); - - ON_CALL(bus, GetSEL()).WillByDefault(Return(true)); - EXPECT_EQ(phase_t::selection, bus.GetPhase()); - - ON_CALL(bus, GetSEL()).WillByDefault(Return(false)); - ON_CALL(bus, GetBSY()).WillByDefault(Return(true)); - - ON_CALL(bus, GetMSG()).WillByDefault(Return(false)); - EXPECT_EQ(phase_t::dataout, bus.GetPhase()); - ON_CALL(bus, GetMSG()).WillByDefault(Return(true)); - EXPECT_EQ(phase_t::reserved, bus.GetPhase()); - - ON_CALL(bus, GetMSG()).WillByDefault(Return(false)); - ON_CALL(bus, GetCD()).WillByDefault(Return(true)); - EXPECT_EQ(phase_t::command, bus.GetPhase()); - - ON_CALL(bus, GetMSG()).WillByDefault(Return(true)); - ON_CALL(bus, GetCD()).WillByDefault(Return(true)); - EXPECT_EQ(phase_t::msgout, bus.GetPhase()); - - ON_CALL(bus, GetMSG()).WillByDefault(Return(false)); - ON_CALL(bus, GetCD()).WillByDefault(Return(false)); - ON_CALL(bus, GetIO()).WillByDefault(Return(true)); - EXPECT_EQ(phase_t::datain, bus.GetPhase()); - - ON_CALL(bus, GetMSG()).WillByDefault(Return(true)); - ON_CALL(bus, GetCD()).WillByDefault(Return(false)); - ON_CALL(bus, GetIO()).WillByDefault(Return(true)); - EXPECT_EQ(phase_t::reserved, bus.GetPhase()); - - ON_CALL(bus, GetMSG()).WillByDefault(Return(true)); - ON_CALL(bus, GetCD()).WillByDefault(Return(true)); - ON_CALL(bus, GetIO()).WillByDefault(Return(true)); - EXPECT_EQ(phase_t::msgin, bus.GetPhase()); - - ON_CALL(bus, GetMSG()).WillByDefault(Return(false)); - ON_CALL(bus, GetCD()).WillByDefault(Return(true)); - ON_CALL(bus, GetIO()).WillByDefault(Return(true)); - EXPECT_EQ(phase_t::status, bus.GetPhase()); -} +#include +#include "buses/bus.h" TEST(BusTest, GetPhaseName) { @@ -75,5 +21,5 @@ TEST(BusTest, GetPhaseName) EXPECT_EQ("STATUS", Bus::GetPhaseName(phase_t::status)); EXPECT_EQ("MESSAGE IN", Bus::GetPhaseName(phase_t::msgin)); EXPECT_EQ("MESSAGE OUT", Bus::GetPhaseName(phase_t::msgout)); - EXPECT_EQ("reserved", Bus::GetPhaseName(phase_t::reserved)); + EXPECT_EQ("???", Bus::GetPhaseName(phase_t::reserved)); } diff --git a/cpp/test/command_context_test.cpp b/cpp/test/command_context_test.cpp index 5d2d137d..ecf6005f 100644 --- a/cpp/test/command_context_test.cpp +++ b/cpp/test/command_context_test.cpp @@ -1,8 +1,8 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // -// Copyright (C) 2022-2023 Uwe Seimet +// Copyright (C) 2022-2024 Uwe Seimet // //--------------------------------------------------------------------------- diff --git a/cpp/test/command_executor_test.cpp b/cpp/test/command_executor_test.cpp index f5a387fb..80bdfd1d 100644 --- a/cpp/test/command_executor_test.cpp +++ b/cpp/test/command_executor_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -23,7 +23,7 @@ TEST(CommandExecutorTest, ProcessDeviceCmd) auto bus = make_shared(); MockAbstractController controller(bus, ID); - auto controller_factory = make_shared(); + auto controller_factory = make_shared(false); auto executor = make_shared(*bus, controller_factory); PbDeviceDefinition definition; PbCommand command; @@ -108,7 +108,7 @@ TEST(CommandExecutorTest, ProcessCmd) { auto bus = make_shared(); MockAbstractController controller(bus, 0); - auto controller_factory = make_shared(); + auto controller_factory = make_shared(false); auto executor = make_shared(*bus, controller_factory); PbCommand command_detach_all; @@ -169,7 +169,7 @@ TEST(CommandExecutorTest, Attach) const DeviceFactory &device_factory = DeviceFactory::Instance(); auto bus = make_shared(); - auto controller_factory = make_shared(); + auto controller_factory = make_shared(false); auto executor = make_shared(*bus, controller_factory); PbDeviceDefinition definition; PbCommand command; @@ -245,7 +245,7 @@ TEST(CommandExecutorTest, Insert) { auto bus = make_shared(); auto [controller, device] = CreateDevice(SCHD); - auto controller_factory = make_shared(); + auto controller_factory = make_shared(false); auto executor = make_shared(*bus, controller_factory); PbDeviceDefinition definition; PbCommand command; @@ -301,7 +301,7 @@ TEST(CommandExecutorTest, Detach) const DeviceFactory &device_factory = DeviceFactory::Instance(); auto bus = make_shared(); - auto controller_factory = make_shared(); + auto controller_factory = make_shared(false); auto executor = make_shared(*bus, controller_factory); PbCommand command; CommandContext context(command, "", ""); @@ -327,7 +327,7 @@ TEST(CommandExecutorTest, DetachAll) const DeviceFactory &device_factory = DeviceFactory::Instance(); auto bus = make_shared(); - auto controller_factory = make_shared(); + auto controller_factory = make_shared(false); auto executor = make_shared(*bus, controller_factory); auto device = device_factory.CreateDevice(SCHS, 0, ""); @@ -343,7 +343,7 @@ TEST(CommandExecutorTest, DetachAll) TEST(CommandExecutorTest, SetReservedIds) { auto bus = make_shared(); - auto controller_factory = make_shared(); + auto controller_factory = make_shared(false); auto executor = make_shared(*bus, controller_factory); string error = executor->SetReservedIds("xyz"); @@ -382,7 +382,7 @@ TEST(CommandExecutorTest, ValidateImageFile) { const DeviceFactory &device_factory = DeviceFactory::Instance(); auto bus = make_shared(); - auto controller_factory = make_shared(); + auto controller_factory = make_shared(false); auto executor = make_shared(*bus, controller_factory); PbCommand command; CommandContext context(command, "", ""); @@ -396,7 +396,7 @@ TEST(CommandExecutorTest, ValidateImageFile) TEST(CommandExecutorTest, PrintCommand) { auto bus = make_shared(); - auto controller_factory = make_shared(); + auto controller_factory = make_shared(false); auto executor = make_shared(*bus, controller_factory); PbDeviceDefinition definition; @@ -422,23 +422,21 @@ TEST(CommandExecutorTest, EnsureLun0) { const DeviceFactory &device_factory = DeviceFactory::Instance(); auto bus = make_shared(); - auto controller_factory = make_shared(); + auto controller_factory = make_shared(false); auto executor = make_shared(*bus, controller_factory); PbCommand command; + CommandContext context(command, "", ""); auto device1 = command.add_devices(); device1->set_unit(0); - string error = executor->EnsureLun0(command); - EXPECT_TRUE(error.empty()); + EXPECT_TRUE(executor->EnsureLun0(context, command)); device1->set_unit(1); - error = executor->EnsureLun0(command); - EXPECT_FALSE(error.empty()); + EXPECT_FALSE(executor->EnsureLun0(context, command)); auto device2 = device_factory.CreateDevice(SCHS, 0, ""); EXPECT_TRUE(controller_factory->AttachToController(*bus, 0, device2)); - error = executor->EnsureLun0(command); - EXPECT_TRUE(error.empty()); + EXPECT_TRUE(executor->EnsureLun0(context, command)); } TEST(CommandExecutorTest, VerifyExistingIdAndLun) @@ -449,7 +447,7 @@ TEST(CommandExecutorTest, VerifyExistingIdAndLun) const DeviceFactory &device_factory = DeviceFactory::Instance(); auto bus = make_shared(); - auto controller_factory = make_shared(); + auto controller_factory = make_shared(false); auto executor = make_shared(*bus, controller_factory); PbCommand command; CommandContext context(command, "", ""); @@ -464,7 +462,7 @@ TEST(CommandExecutorTest, VerifyExistingIdAndLun) TEST(CommandExecutorTest, CreateDevice) { auto bus = make_shared(); - auto controller_factory = make_shared(); + auto controller_factory = make_shared(false); auto executor = make_shared(*bus, controller_factory); PbCommand command; CommandContext context(command, "", ""); @@ -481,7 +479,7 @@ TEST(CommandExecutorTest, CreateDevice) TEST(CommandExecutorTest, SetSectorSize) { auto bus = make_shared(); - auto controller_factory = make_shared(); + auto controller_factory = make_shared(false); auto executor = make_shared(*bus, controller_factory); PbCommand command; CommandContext context(command, "", ""); @@ -504,7 +502,7 @@ TEST(CommandExecutorTest, SetSectorSize) TEST(CommandExecutorTest, ValidateOperationAgainstDevice) { auto bus = make_shared(); - auto controller_factory = make_shared(); + auto controller_factory = make_shared(false); auto executor = make_shared(*bus, controller_factory); PbCommand command; CommandContext context(command, "", ""); @@ -557,7 +555,7 @@ TEST(CommandExecutorTest, ValidateOperationAgainstDevice) TEST(CommandExecutorTest, ValidateIdAndLun) { auto bus = make_shared(); - auto controller_factory = make_shared(); + auto controller_factory = make_shared(false); auto executor = make_shared(*bus, controller_factory); PbCommand command; CommandContext context(command, "", ""); @@ -573,7 +571,7 @@ TEST(CommandExecutorTest, ValidateIdAndLun) TEST(CommandExecutorTest, SetProductData) { auto bus = make_shared(); - auto controller_factory = make_shared(); + auto controller_factory = make_shared(false); auto executor = make_shared(*bus, controller_factory); PbCommand command; CommandContext context(command, "", ""); diff --git a/cpp/test/command_response_test.cpp b/cpp/test/command_response_test.cpp index 1ec6339e..40559498 100644 --- a/cpp/test/command_response_test.cpp +++ b/cpp/test/command_response_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -28,7 +28,7 @@ TEST(CommandResponseTest, Operation_Count) void TestNonDiskDevice(PbDeviceType type, unsigned int default_param_count) { auto bus = make_shared(); - ControllerFactory controller_factory; + ControllerFactory controller_factory(false); CommandResponse response; auto d = DeviceFactory::Instance().CreateDevice(type, 0, ""); @@ -112,7 +112,7 @@ TEST(CommandResponseTest, GetDevicesInfo) const int LUN3 = 6; auto bus = make_shared(); - ControllerFactory controller_factory; + ControllerFactory controller_factory(false); CommandResponse response; PbCommand command; diff --git a/cpp/test/controller_factory_test.cpp b/cpp/test/controller_factory_test.cpp index b78e622a..ba4ee7ac 100644 --- a/cpp/test/controller_factory_test.cpp +++ b/cpp/test/controller_factory_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -15,48 +15,64 @@ TEST(ControllerFactoryTest, LifeCycle) const int ID1 = 4; const int ID2 = 5; const int LUN1 = 0; - const int LUN2 = 3; + const int LUN2 = 1; auto bus = make_shared(); - ControllerFactory controller_factory; + ControllerFactory controller_factory_scsi(false); const DeviceFactory &device_factory = DeviceFactory::Instance(); auto device = device_factory.CreateDevice(SCHS, -1, ""); - EXPECT_FALSE(controller_factory.AttachToController(*bus, ID1, device)); + EXPECT_FALSE(controller_factory_scsi.AttachToController(*bus, ID1, device)); device = device_factory.CreateDevice(SCHS, LUN1, ""); - EXPECT_TRUE(controller_factory.AttachToController(*bus, ID1, device)); - EXPECT_TRUE(controller_factory.HasController(ID1)); - auto controller = controller_factory.FindController(ID1); + EXPECT_TRUE(controller_factory_scsi.AttachToController(*bus, ID1, device)); + EXPECT_TRUE(controller_factory_scsi.HasController(ID1)); + auto controller = controller_factory_scsi.FindController(ID1); EXPECT_NE(nullptr, controller); - EXPECT_EQ(1, controller->GetLunCount()); - EXPECT_FALSE(controller_factory.HasController(0)); - EXPECT_EQ(nullptr, controller_factory.FindController(0)); - EXPECT_TRUE(controller_factory.HasDeviceForIdAndLun(ID1, LUN1)); - EXPECT_NE(nullptr, controller_factory.GetDeviceForIdAndLun(ID1, LUN1)); - EXPECT_FALSE(controller_factory.HasDeviceForIdAndLun(0, 0)); - EXPECT_EQ(nullptr, controller_factory.GetDeviceForIdAndLun(0, 0)); + EXPECT_EQ(1U, controller->GetLunCount()); + EXPECT_FALSE(controller_factory_scsi.HasController(0)); + EXPECT_EQ(nullptr, controller_factory_scsi.FindController(0)); + EXPECT_TRUE(controller_factory_scsi.HasDeviceForIdAndLun(ID1, LUN1)); + EXPECT_NE(nullptr, controller_factory_scsi.GetDeviceForIdAndLun(ID1, LUN1)); + EXPECT_FALSE(controller_factory_scsi.HasDeviceForIdAndLun(0, 0)); + EXPECT_EQ(nullptr, controller_factory_scsi.GetDeviceForIdAndLun(0, 0)); device = device_factory.CreateDevice(SCHS, LUN2, ""); - EXPECT_TRUE(controller_factory.AttachToController(*bus, ID1, device)); - EXPECT_TRUE(controller_factory.HasController(ID1)); - controller = controller_factory.FindController(ID1); - EXPECT_NE(nullptr, controller_factory.FindController(ID1)); - EXPECT_TRUE(controller_factory.DeleteController(*controller)); - EXPECT_EQ(nullptr, controller_factory.FindController(ID1)); + EXPECT_TRUE(controller_factory_scsi.AttachToController(*bus, ID1, device)); + EXPECT_TRUE(controller_factory_scsi.HasController(ID1)); + controller = controller_factory_scsi.FindController(ID1); + EXPECT_NE(nullptr, controller_factory_scsi.FindController(ID1)); + EXPECT_TRUE(controller_factory_scsi.DeleteController(*controller)); + EXPECT_EQ(nullptr, controller_factory_scsi.FindController(ID1)); auto disk = make_shared(); - EXPECT_TRUE(controller_factory.AttachToController(*bus, ID2, disk)); + EXPECT_TRUE(controller_factory_scsi.AttachToController(*bus, ID2, disk)); EXPECT_CALL(*disk, FlushCache); - controller_factory.DeleteAllControllers(); - EXPECT_FALSE(controller_factory.HasController(ID1)); - EXPECT_EQ(nullptr, controller_factory.FindController(ID1)); - EXPECT_EQ(nullptr, controller_factory.GetDeviceForIdAndLun(ID1, LUN1)); - EXPECT_FALSE(controller_factory.HasDeviceForIdAndLun(ID1, LUN1)); - EXPECT_FALSE(controller_factory.HasController(ID2)); - EXPECT_EQ(nullptr, controller_factory.FindController(ID2)); - EXPECT_EQ(nullptr, controller_factory.GetDeviceForIdAndLun(ID2, LUN1)); - EXPECT_FALSE(controller_factory.HasDeviceForIdAndLun(ID2, LUN1)); + controller_factory_scsi.DeleteAllControllers(); + EXPECT_FALSE(controller_factory_scsi.HasController(ID1)); + EXPECT_EQ(nullptr, controller_factory_scsi.FindController(ID1)); + EXPECT_EQ(nullptr, controller_factory_scsi.GetDeviceForIdAndLun(ID1, LUN1)); + EXPECT_FALSE(controller_factory_scsi.HasDeviceForIdAndLun(ID1, LUN1)); + EXPECT_FALSE(controller_factory_scsi.HasController(ID2)); + EXPECT_EQ(nullptr, controller_factory_scsi.FindController(ID2)); + EXPECT_EQ(nullptr, controller_factory_scsi.GetDeviceForIdAndLun(ID2, LUN1)); + EXPECT_FALSE(controller_factory_scsi.HasDeviceForIdAndLun(ID2, LUN1)); + + device = device_factory.CreateDevice(SAHD, LUN1, ""); + EXPECT_FALSE(controller_factory_scsi.AttachToController(*bus, ID1, device)); + EXPECT_FALSE(controller_factory_scsi.HasController(ID1)); + + ControllerFactory controller_factory_sasi(true); + EXPECT_TRUE(controller_factory_sasi.AttachToController(*bus, ID1, device)); + EXPECT_TRUE(controller_factory_sasi.HasController(ID1)); + + device = device_factory.CreateDevice(SAHD, 7, ""); + EXPECT_FALSE(controller_factory_sasi.AttachToController(*bus, ID2, device)); + EXPECT_FALSE(controller_factory_sasi.HasController(ID2)); + + device = device_factory.CreateDevice(SCHD, LUN2, ""); + EXPECT_FALSE(controller_factory_sasi.AttachToController(*bus, ID2, device)); + EXPECT_FALSE(controller_factory_sasi.HasController(ID2)); } TEST(ControllerFactoryTest, AttachToController) @@ -66,7 +82,7 @@ TEST(ControllerFactoryTest, AttachToController) const int LUN2 = 0; auto bus = make_shared(); - ControllerFactory controller_factory; + ControllerFactory controller_factory(false); const DeviceFactory &device_factory = DeviceFactory::Instance(); auto device1 = device_factory.CreateDevice(SCHS, LUN1, ""); @@ -80,7 +96,7 @@ TEST(ControllerFactoryTest, AttachToController) TEST(ControllerFactory, ProcessOnController) { - ControllerFactory controller_factory; + ControllerFactory controller_factory(false); EXPECT_EQ(AbstractController::shutdown_mode::none, controller_factory.ProcessOnController(0)); } diff --git a/cpp/test/controller_test.cpp b/cpp/test/controller_test.cpp new file mode 100644 index 00000000..1284e47b --- /dev/null +++ b/cpp/test/controller_test.cpp @@ -0,0 +1,198 @@ +//--------------------------------------------------------------------------- +// +// SCSI device emulator and SCSI tools for the Raspberry Pi +// +// Copyright (C) 2022-2024 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#include "mocks.h" +#include "shared/shared_exceptions.h" + +using namespace scsi_defs; + +TEST(ControllerTest, Reset) +{ + const int TARGET_ID = 5; + const int INITIATOR_ID = 7; + + NiceMock bus; + auto controller = make_shared(bus, TARGET_ID, 32); + auto device = make_shared(0); + + controller->Init(); + controller->AddDevice(device); + + controller->ProcessOnController((1 << TARGET_ID) + (1 << INITIATOR_ID)); + EXPECT_EQ(INITIATOR_ID, controller->GetInitiatorId()); + controller->Reset(); + EXPECT_EQ(-1, controller->GetInitiatorId()); +} + +TEST(ControllerTest, GetInitiatorId) +{ + const int TARGET_ID = 0; + const int INITIATOR_ID = 2; + + auto bus = make_shared>(); + MockController controller(bus, TARGET_ID); + auto device = make_shared(0); + + controller.Init(); + controller.AddDevice(device); + + controller.ProcessOnController((1 << TARGET_ID) + (1 << INITIATOR_ID)); + EXPECT_EQ(INITIATOR_ID, controller.GetInitiatorId()); +} + +TEST(ControllerTest, BusFree) +{ + auto bus = make_shared>(); + MockController controller(bus, 0); + + controller.SetPhase(phase_t::busfree); + controller.BusFree(); + EXPECT_EQ(phase_t::busfree, controller.GetPhase()); + + controller.SetStatus(status::check_condition); + controller.SetPhase(phase_t::reserved); + controller.BusFree(); + EXPECT_EQ(phase_t::busfree, controller.GetPhase()); + EXPECT_EQ(status::good, controller.GetStatus()); + + controller.ScheduleShutdown(AbstractController::shutdown_mode::none); + controller.SetPhase(phase_t::reserved); + controller.BusFree(); + + controller.ScheduleShutdown(AbstractController::shutdown_mode::stop_pi); + controller.SetPhase(phase_t::reserved); + controller.BusFree(); + + controller.ScheduleShutdown(AbstractController::shutdown_mode::restart_pi); + controller.SetPhase(phase_t::reserved); + controller.BusFree(); + + controller.ScheduleShutdown(AbstractController::shutdown_mode::stop_s2p); + controller.SetPhase(phase_t::reserved); + controller.BusFree(); +} + +TEST(ControllerTest, Selection) +{ + auto bus = make_shared>(); + auto controller = make_shared(bus, 0); + auto device = make_shared(0); + + controller->AddDevice(device); + + controller->SetPhase(phase_t::selection); + controller->Selection(); + EXPECT_EQ(phase_t::selection, controller->GetPhase()); + + controller->Selection(); + EXPECT_EQ(phase_t::selection, controller->GetPhase()); + + ON_CALL(*bus, GetDAT).WillByDefault(Return(1)); + controller->Selection(); + EXPECT_EQ(phase_t::selection, controller->GetPhase()); +} + +TEST(ControllerTest, Command) +{ + auto bus = make_shared>(); + MockController controller(bus, 0); + auto device = make_shared(0); + + controller.AddDevice(device); + + controller.SetPhase(phase_t::command); + EXPECT_CALL(controller, Status).Times(2); + controller.Command(); + EXPECT_EQ(phase_t::command, controller.GetPhase()); + + controller.SetPhase(phase_t::reserved); + controller.Command(); + EXPECT_EQ(phase_t::command, controller.GetPhase()); + + controller.SetPhase(phase_t::reserved); + controller.Command(); + EXPECT_EQ(phase_t::command, controller.GetPhase()); +} + +TEST(ControllerTest, MsgIn) +{ + auto bus = make_shared>(); + MockController controller(bus, 0); + + controller.SetPhase(phase_t::reserved); + controller.MsgIn(); + EXPECT_EQ(phase_t::msgin, controller.GetPhase()); + EXPECT_EQ(0, controller.GetOffset()); + EXPECT_EQ(0, controller.GetCurrentLength()); +} + +TEST(ControllerTest, MsgOut) +{ + auto bus = make_shared>(); + MockController controller(bus, 0); + + controller.SetPhase(phase_t::reserved); + controller.MsgOut(); + EXPECT_EQ(phase_t::msgout, controller.GetPhase()); + EXPECT_EQ(0, controller.GetOffset()); + EXPECT_EQ(1, controller.GetCurrentLength()); +} + +TEST(ControllerTest, DataIn) +{ + auto bus = make_shared>(); + MockController controller(bus, 0); + + controller.SetPhase(phase_t::reserved); + controller.SetCurrentLength(0); + EXPECT_CALL(controller, Status); + controller.DataIn(); + EXPECT_EQ(phase_t::reserved, controller.GetPhase()); + + controller.SetCurrentLength(1); + controller.DataIn(); + EXPECT_EQ(phase_t::datain, controller.GetPhase()); + EXPECT_EQ(0, controller.GetOffset()); +} + +TEST(ControllerTest, DataOut) +{ + auto bus = make_shared>(); + MockController controller(bus, 0); + + controller.SetPhase(phase_t::reserved); + controller.SetCurrentLength(0); + EXPECT_CALL(controller, Status); + controller.DataOut(); + EXPECT_EQ(phase_t::reserved, controller.GetPhase()); + + controller.SetCurrentLength(1); + controller.DataOut(); + EXPECT_EQ(phase_t::dataout, controller.GetPhase()); + EXPECT_EQ(0, controller.GetOffset()); +} + +TEST(ControllerTest, RequestSense) +{ + auto bus = make_shared>(); + auto controller = make_shared(bus, 0); + auto device = make_shared(0); + EXPECT_TRUE(device->Init( { })); + + controller->AddDevice(device); + + // ALLOCATION LENGTH + controller->SetCdbByte(4, 255); + // Non-existing LUN + controller->SetCdbByte(1, 0x20); + + device->SetReady(true); + EXPECT_CALL(*controller, Status); + EXPECT_NO_THROW(device->Dispatch(scsi_command::cmd_request_sense)); + EXPECT_EQ(status::good, controller->GetStatus()) << "Wrong CHECK CONDITION for non-existing LUN"; +} diff --git a/cpp/test/daynaport_test.cpp b/cpp/test/daynaport_test.cpp index 37142257..e2f00dcd 100644 --- a/cpp/test/daynaport_test.cpp +++ b/cpp/test/daynaport_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -8,37 +8,39 @@ #include "mocks.h" #include "shared/shared_exceptions.h" -#include "base/device_factory.h" #include "devices/daynaport.h" TEST(ScsiDaynaportTest, Device_Defaults) { - auto device = DeviceFactory::Instance().CreateDevice(UNDEFINED, 0, "daynaport"); - EXPECT_NE(nullptr, device); - EXPECT_EQ(SCDP, device->GetType()); - EXPECT_FALSE(device->SupportsFile()); - EXPECT_TRUE(device->SupportsParams()); - EXPECT_FALSE(device->IsProtectable()); - EXPECT_FALSE(device->IsProtected()); - EXPECT_FALSE(device->IsReadOnly()); - EXPECT_FALSE(device->IsRemovable()); - EXPECT_FALSE(device->IsRemoved()); - EXPECT_FALSE(device->IsLockable()); - EXPECT_FALSE(device->IsLocked()); - EXPECT_FALSE(device->IsStoppable()); - EXPECT_FALSE(device->IsStopped()); - - EXPECT_EQ("Dayna", device->GetVendor()); - EXPECT_EQ("SCSI/Link", device->GetProduct()); - EXPECT_EQ("1.4a", device->GetRevision()); + DaynaPort daynaport(0); + + EXPECT_EQ(SCDP, daynaport.GetType()); + EXPECT_FALSE(daynaport.SupportsFile()); + EXPECT_TRUE(daynaport.SupportsParams()); + EXPECT_FALSE(daynaport.IsProtectable()); + EXPECT_FALSE(daynaport.IsProtected()); + EXPECT_FALSE(daynaport.IsReadOnly()); + EXPECT_FALSE(daynaport.IsRemovable()); + EXPECT_FALSE(daynaport.IsRemoved()); + EXPECT_FALSE(daynaport.IsLockable()); + EXPECT_FALSE(daynaport.IsLocked()); + EXPECT_FALSE(daynaport.IsStoppable()); + EXPECT_FALSE(daynaport.IsStopped()); + + EXPECT_EQ("Dayna", daynaport.GetVendor()); + EXPECT_EQ("SCSI/Link", daynaport.GetProduct()); + EXPECT_EQ("1.4a", daynaport.GetRevision()); } TEST(ScsiDaynaportTest, GetDefaultParams) { - const auto [controller, daynaport] = CreateDevice(SCDP); + DaynaPort daynaport(0); - const auto params = daynaport->GetDefaultParams(); - EXPECT_EQ(2U, params.size()); + const auto params = daynaport.GetDefaultParams(); + EXPECT_EQ(3U, params.size()); + EXPECT_TRUE(params.contains("interface")); + EXPECT_TRUE(params.contains("inet")); + EXPECT_TRUE(params.contains("bridge")); } TEST(ScsiDaynaportTest, Inquiry) @@ -57,18 +59,19 @@ TEST(ScsiDaynaportTest, TestUnitReady) TEST(ScsiDaynaportTest, Read) { - auto [controller, daynaport] = CreateDevice(SCDP); + DaynaPort daynaport(0); + vector cdb(6); // ALLOCATION LENGTH - controller->SetCdbByte(4, 1); - vector buf(0); - EXPECT_EQ(0, dynamic_pointer_cast(daynaport)->Read(controller->GetCdb(), buf, 0)) - << "Trying to read the root sector must fail"; + cdb[4] = 1; + vector buf; + EXPECT_EQ(0, daynaport.Read(cdb, buf, 0)) << "Trying to read the root sector must fail"; } TEST(ScsiDaynaportTest, Write) { auto [controller, daynaport] = CreateDevice(SCDP); + vector cdb(6); // Unknown data format controller->SetCdbByte(5, 0xff); @@ -176,7 +179,6 @@ TEST(ScsiDaynaportTest, EnableInterface) TEST(ScsiDaynaportTest, GetDelayAfterBytes) { DaynaPort daynaport(0); - daynaport.Init( { }); EXPECT_EQ(6, daynaport.GetDelayAfterBytes()); } diff --git a/cpp/test/device_factory_test.cpp b/cpp/test/device_factory_test.cpp index f36f2b6d..e039ed23 100644 --- a/cpp/test/device_factory_test.cpp +++ b/cpp/test/device_factory_test.cpp @@ -1,14 +1,67 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // //--------------------------------------------------------------------------- #include +#if defined BUILD_SCHD || defined BUILD_SCRM +#include "devices/scsi_hd.h" +#endif +#ifdef BUILD_SCMO +#include "devices/optical_memory.h" +#endif +#ifdef BUILD_SCCD +#include "devices/scsi_cd.h" +#endif +#ifdef BUILD_SCDP +#include "devices/daynaport.h" +#endif +#ifdef BUILD_SCLP +#include "devices/printer.h" +#endif +#ifdef BUILD_SCHS +#include "devices/host_services.h" +#endif +#ifdef BUILD_SAHD +#include "devices/sasi_hd.h" +#endif #include "base/device_factory.h" +TEST(DeviceFactoryTest, CreateDevice) +{ + const DeviceFactory &device_factory = DeviceFactory::Instance(); + +#ifdef BUILD_SCHD + EXPECT_NE(nullptr, dynamic_pointer_cast(device_factory.CreateDevice(SCHD, 0, ""))); +#endif +#ifdef BUILD_SCRM + EXPECT_NE(nullptr, dynamic_pointer_cast(device_factory.CreateDevice(SCRM, 0, ""))); +#endif +#ifdef BUILD_SCMO + EXPECT_NE(nullptr, dynamic_pointer_cast(device_factory.CreateDevice(SCMO, 0, ""))); +#endif +#ifdef BUILD_SCCD + EXPECT_NE(nullptr, dynamic_pointer_cast(device_factory.CreateDevice(SCCD, 0, ""))); +#endif +#ifdef BUILD_SCDP + EXPECT_NE(nullptr, dynamic_pointer_cast(device_factory.CreateDevice(SCDP, 0, ""))); +#endif +#ifdef BUILD_SCLP + EXPECT_NE(nullptr, dynamic_pointer_cast(device_factory.CreateDevice(SCHS, 0, ""))); +#endif +#ifdef BUILD_SCHS + EXPECT_NE(nullptr, dynamic_pointer_cast(device_factory.CreateDevice(SCLP, 0, ""))); +#endif +#ifdef BUILD_SAHD + EXPECT_NE(nullptr, dynamic_pointer_cast(device_factory.CreateDevice(SAHD, 0, ""))); +#endif + + EXPECT_EQ(nullptr, device_factory.CreateDevice(UNDEFINED, 0, "")); +} + TEST(DeviceFactoryTest, GetTypeForFile) { const DeviceFactory &device_factory = DeviceFactory::Instance(); @@ -33,17 +86,17 @@ TEST(DeviceFactoryTest, GetTypeForFile) TEST(DeviceFactoryTest, GetExtensionMapping) { - auto mapping = DeviceFactory::Instance().GetExtensionMapping(); + const auto &mapping = DeviceFactory::Instance().GetExtensionMapping(); EXPECT_EQ(9U, mapping.size()); - EXPECT_EQ(SCHD, mapping["hd1"]); - EXPECT_EQ(SCHD, mapping["hds"]); - EXPECT_EQ(SCHD, mapping["hda"]); - EXPECT_EQ(SCRM, mapping["hdr"]); - EXPECT_EQ(SCMO, mapping["mos"]); - EXPECT_EQ(SCCD, mapping["iso"]); - EXPECT_EQ(SCCD, mapping["cdr"]); - EXPECT_EQ(SCCD, mapping["toast"]); - EXPECT_EQ(SCCD, mapping["is1"]); + EXPECT_EQ(SCHD, mapping.at("hd1")); + EXPECT_EQ(SCHD, mapping.at("hds")); + EXPECT_EQ(SCHD, mapping.at("hda")); + EXPECT_EQ(SCRM, mapping.at("hdr")); + EXPECT_EQ(SCMO, mapping.at("mos")); + EXPECT_EQ(SCCD, mapping.at("iso")); + EXPECT_EQ(SCCD, mapping.at("cdr")); + EXPECT_EQ(SCCD, mapping.at("toast")); + EXPECT_EQ(SCCD, mapping.at("is1")); } TEST(DeviceFactoryTest, AddExtensionMapping) @@ -59,9 +112,3 @@ TEST(DeviceFactoryTest, AddExtensionMapping) EXPECT_EQ(10U, mapping.size()); EXPECT_EQ(SCCD, mapping["ext"]); } - -TEST(DeviceFactoryTest, UnknownDeviceType) -{ - auto device = DeviceFactory::Instance().CreateDevice(UNDEFINED, 0, "test"); - EXPECT_EQ(nullptr, device); -} diff --git a/cpp/test/device_test.cpp b/cpp/test/device_test.cpp index 726daae0..7cf6c730 100644 --- a/cpp/test/device_test.cpp +++ b/cpp/test/device_test.cpp @@ -1,13 +1,12 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2023 Uwe Seimet // //--------------------------------------------------------------------------- #include "mocks.h" -#include "base/device.h" TEST(DeviceTest, GetDefaultParams) { diff --git a/cpp/test/disk_cache_test.cpp b/cpp/test/disk_cache_test.cpp index c973055b..1dd81a19 100644 --- a/cpp/test/disk_cache_test.cpp +++ b/cpp/test/disk_cache_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2024 Uwe Seimet // diff --git a/cpp/test/disk_test.cpp b/cpp/test/disk_test.cpp index 72085e42..593d059e 100644 --- a/cpp/test/disk_test.cpp +++ b/cpp/test/disk_test.cpp @@ -1,13 +1,13 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // //--------------------------------------------------------------------------- #include "mocks.h" -#include "test_shared.h" +#include "shared/shared_exceptions.h" #include "base/memory_util.h" #include "devices/disk.h" @@ -23,23 +23,6 @@ pair, shared_ptr> CreateDisk() return {controller, disk}; } -TEST(DiskTest, SetUpCache) -{ - MockDisk disk; - - EXPECT_FALSE(disk.SetUpCache()); - - disk.SetBlockCount(1); - disk.SetSectorSizeInBytes(512); - EXPECT_FALSE(disk.SetUpCache()); - - disk.SetCachingMode(PbCachingMode::WRITE_THROUGH); - EXPECT_FALSE(disk.SetUpCache()); - - disk.SetFilename(CreateTempFile(512).string()); - EXPECT_TRUE(disk.SetUpCache()); -} - TEST(DiskTest, Dispatch) { auto [controller, disk] = CreateDisk(); @@ -52,8 +35,7 @@ TEST(DiskTest, Dispatch) EXPECT_EQ(status::good, controller->GetStatus()); disk->SetMediumChanged(true); - EXPECT_CALL(*controller, Error); - EXPECT_NO_THROW(disk->Dispatch(scsi_command::cmd_test_unit_ready)); + EXPECT_THROW(disk->Dispatch(scsi_command::cmd_test_unit_ready), scsi_exception); EXPECT_FALSE(disk->IsMediumChanged()); } @@ -209,8 +191,6 @@ TEST(DiskTest, Read6) asc::lba_out_of_range, "READ(6) must fail for a medium with 0 blocks"); EXPECT_EQ(0U, disk->GetNextSector()); - - // Further testing requires filesystem access } TEST(DiskTest, Read10) @@ -220,14 +200,14 @@ TEST(DiskTest, Read10) TestShared::Dispatch(*disk, scsi_command::cmd_read10, sense_key::illegal_request, asc::lba_out_of_range, "READ(10) must fail for a medium with 0 blocks"); + EXPECT_EQ(0U, disk->GetNextSector()); + disk->SetBlockCount(1); EXPECT_CALL(*controller, Status); EXPECT_NO_THROW(disk->Dispatch(scsi_command::cmd_read10)); EXPECT_EQ(status::good, controller->GetStatus()); EXPECT_EQ(0U, disk->GetNextSector()); - - // Further testing requires filesystem access } TEST(DiskTest, Read16) @@ -237,15 +217,12 @@ TEST(DiskTest, Read16) TestShared::Dispatch(*disk, scsi_command::cmd_read16, sense_key::illegal_request, asc::lba_out_of_range, "READ(16) must fail for a medium with 0 blocks"); - disk->SetBlockCount(1); EXPECT_CALL(*controller, Status); EXPECT_NO_THROW(disk->Dispatch(scsi_command::cmd_read16)); EXPECT_EQ(status::good, controller->GetStatus()); EXPECT_EQ(0U, disk->GetNextSector()); - - // Further testing requires filesystem access } TEST(DiskTest, Write6) @@ -263,8 +240,6 @@ TEST(DiskTest, Write6) asc::write_protected, "WRITE(6) must fail because drive is write-protected"); EXPECT_EQ(0U, disk->GetNextSector()); - - // Further testing requires filesystem access } TEST(DiskTest, Write10) @@ -280,8 +255,6 @@ TEST(DiskTest, Write10) EXPECT_EQ(status::good, controller->GetStatus()); EXPECT_EQ(0U, disk->GetNextSector()); - - // Further testing requires filesystem access } TEST(DiskTest, Write16) @@ -297,8 +270,6 @@ TEST(DiskTest, Write16) EXPECT_EQ(status::good, controller->GetStatus()); EXPECT_EQ(0U, disk->GetNextSector()); - - // Further testing requires filesystem access } TEST(DiskTest, Verify10) @@ -315,8 +286,6 @@ TEST(DiskTest, Verify10) EXPECT_CALL(*controller, Status); EXPECT_NO_THROW(disk->Dispatch(scsi_command::cmd_verify10)); EXPECT_EQ(status::good, controller->GetStatus()); - - // Further testing requires filesystem access } TEST(DiskTest, Verify16) @@ -333,14 +302,13 @@ TEST(DiskTest, Verify16) EXPECT_CALL(*controller, Status); EXPECT_NO_THROW(disk->Dispatch(scsi_command::cmd_verify16)); EXPECT_EQ(status::good, controller->GetStatus()); - - // Further testing requires filesystem access } TEST(DiskTest, ReadLong10) { auto [controller, disk] = CreateDisk(); + EXPECT_CALL(*disk, FlushCache); EXPECT_CALL(*controller, Status); EXPECT_NO_THROW(disk->Dispatch(scsi_command::cmd_read_long10)); EXPECT_EQ(status::good, controller->GetStatus()); @@ -358,8 +326,6 @@ TEST(DiskTest, ReadLong10) controller->SetCdbByte(7, 255); TestShared::Dispatch(*disk, scsi_command::cmd_read_long10, sense_key::illegal_request, asc::invalid_field_in_cdb, "READ LONG(10) must fail because it only supports a limited transfer length"); - - // Further testing requires filesystem access } TEST(DiskTest, ReadLong16) @@ -369,6 +335,7 @@ TEST(DiskTest, ReadLong16) // READ LONG(16), not READ CAPACITY(16) controller->SetCdbByte(1, 0x11); + EXPECT_CALL(*disk, FlushCache); EXPECT_CALL(*controller, Status); EXPECT_NO_THROW(disk->Dispatch(scsi_command::cmd_read_capacity16_read_long16)); EXPECT_EQ(status::good, controller->GetStatus()); @@ -381,14 +348,13 @@ TEST(DiskTest, ReadLong16) controller->SetCdbByte(12, 55); TestShared::Dispatch(*disk, scsi_command::cmd_read_capacity16_read_long16, sense_key::illegal_request, asc::invalid_field_in_cdb, "READ LONG(16) must fail because it only supports a limited transfer length"); - - // Further testing requires filesystem access } TEST(DiskTest, WriteLong10) { auto [controller, disk] = CreateDisk(); + EXPECT_CALL(*disk, FlushCache); EXPECT_CALL(*controller, Status); EXPECT_NO_THROW(disk->Dispatch(scsi_command::cmd_write_long10)); EXPECT_EQ(status::good, controller->GetStatus()); @@ -406,8 +372,6 @@ TEST(DiskTest, WriteLong10) controller->SetCdbByte(7, 255); TestShared::Dispatch(*disk, scsi_command::cmd_write_long10, sense_key::illegal_request, asc::invalid_field_in_cdb, "WRITE LONG(10) must fail because it only supports a limited transfer length"); - - // Further testing requires filesystem access } TEST(DiskTest, WriteLong16) @@ -419,6 +383,7 @@ TEST(DiskTest, WriteLong16) asc::lba_out_of_range, "WRITE LONG(16) must fail because the capacity is exceeded"); controller->SetCdbByte(2, 0); + EXPECT_CALL(*disk, FlushCache); EXPECT_CALL(*controller, Status); EXPECT_NO_THROW(disk->Dispatch(scsi_command::cmd_write_long16)); EXPECT_EQ(status::good, controller->GetStatus()); @@ -426,8 +391,6 @@ TEST(DiskTest, WriteLong16) controller->SetCdbByte(12, 255); TestShared::Dispatch(*disk, scsi_command::cmd_write_long16, sense_key::illegal_request, asc::invalid_field_in_cdb, "WRITE LONG(16) must fail because it only supports a limited transfer length"); - - // Further testing requires filesystem access } TEST(DiskTest, StartStopUnit) @@ -456,13 +419,13 @@ TEST(DiskTest, StartStopUnit) disk->SetReady(false); EXPECT_CALL(*disk, FlushCache).Times(0); TestShared::Dispatch(*disk, scsi_command::cmd_start_stop, sense_key::illegal_request, - asc::load_or_eject_failed, "START/STOP must fail"); + asc::load_or_eject_failed, "START/STOP must fail because drive is not ready"); disk->SetReady(true); disk->SetLocked(true); EXPECT_CALL(*disk, FlushCache).Times(0); TestShared::Dispatch(*disk, scsi_command::cmd_start_stop, sense_key::illegal_request, - asc::load_or_eject_failed, "LOAD/EJECT must fail"); + asc::load_or_eject_failed, "LOAD/EJECT must fail because drive is locked"); // Start/Unload controller->SetCdbByte(4, 0x01); @@ -476,6 +439,23 @@ TEST(DiskTest, StartStopUnit) EXPECT_CALL(*controller, Status); EXPECT_NO_THROW(disk->Dispatch(scsi_command::cmd_start_stop)); EXPECT_EQ(status::good, controller->GetStatus()); + + // Start/Load with previous medium + controller->SetCdbByte(4, 0x02); + disk->SetLocked(false); + disk->SetFilename("filename"); + EXPECT_CALL(*controller, Status); + EXPECT_CALL(*disk, FlushCache); + // Eject existing medium + EXPECT_NO_THROW(disk->Dispatch(scsi_command::cmd_start_stop)); + EXPECT_EQ(status::good, controller->GetStatus()); + EXPECT_TRUE(disk->GetFilename().empty()); + // Re-load medium + controller->SetCdbByte(4, 0x03); + EXPECT_CALL(*controller, Status); + EXPECT_NO_THROW(disk->Dispatch(scsi_command::cmd_start_stop)); + EXPECT_EQ(status::good, controller->GetStatus()); + EXPECT_EQ("filename", disk->GetFilename()); } TEST(DiskTest, PreventAllowMediumRemoval) @@ -655,24 +635,49 @@ TEST(DiskTest, EvaluateBlockDescriptors) int sector_size = 512; MockDisk disk; - EXPECT_THAT([&] { disk.EvaluateBlockDescriptors(scsi_command::cmd_mode_select6, buf, 0, sector_size) ; }, + EXPECT_THAT([&] {disk.EvaluateBlockDescriptors(scsi_command::cmd_mode_select6, {}, sector_size);}, + Throws(AllOf( + Property(&scsi_exception::get_sense_key, sense_key::illegal_request), + Property(&scsi_exception::get_asc, asc::parameter_list_length_error)))) + << "Parameter list is too short"; + EXPECT_EQ(512, sector_size); + + EXPECT_THAT([&] {disk.EvaluateBlockDescriptors(scsi_command::cmd_mode_select6, {}, sector_size);}, + Throws(AllOf( + Property(&scsi_exception::get_sense_key, sense_key::illegal_request), + Property(&scsi_exception::get_asc, asc::parameter_list_length_error)))) + << "Parameter list is too short"; + EXPECT_EQ(512, sector_size); + + EXPECT_THAT([&] {disk.EvaluateBlockDescriptors(scsi_command::cmd_mode_select10, {}, sector_size);}, Throws(AllOf( Property(&scsi_exception::get_sense_key, sense_key::illegal_request), - Property(&scsi_exception::get_asc, asc::parameter_list_length_error)))) << "Parameter list is invalid"; + Property(&scsi_exception::get_asc, asc::parameter_list_length_error)))) + << "Parameter list is too short"; + EXPECT_EQ(512, sector_size); - EXPECT_THAT([&] {disk.EvaluateBlockDescriptors(scsi_command::cmd_mode_select10, buf, 0, sector_size);}, + EXPECT_THAT([&] {disk.EvaluateBlockDescriptors(scsi_command::cmd_mode_select10, {}, sector_size);}, Throws(AllOf( Property(&scsi_exception::get_sense_key, sense_key::illegal_request), - Property(&scsi_exception::get_asc, asc::parameter_list_length_error)))) << "Parameter list is invalid"; + Property(&scsi_exception::get_asc, asc::parameter_list_length_error)))) + << "Parameter list is too short"; + EXPECT_EQ(512, sector_size); buf = CreateParameters("00:00:00:04:00:00:00:00:00:00:08:00"); - EXPECT_NO_THROW(disk.EvaluateBlockDescriptors(scsi_command::cmd_mode_select6, buf, buf.size(), sector_size)); + EXPECT_NO_THROW(disk.EvaluateBlockDescriptors(scsi_command::cmd_mode_select6, buf, sector_size)); EXPECT_EQ(2048, sector_size); - sector_size = 512; + + buf = CreateParameters("00:00:00:04:00:00:00:00:00:00:08:04"); + EXPECT_NO_THROW(disk.EvaluateBlockDescriptors(scsi_command::cmd_mode_select6, buf, sector_size)); + EXPECT_EQ(2052, sector_size); buf = CreateParameters("00:00:00:00:00:00:00:08:00:08:00:00:00:00:04:00"); - EXPECT_NO_THROW(disk.EvaluateBlockDescriptors(scsi_command::cmd_mode_select10, buf, buf.size(), sector_size)); + EXPECT_NO_THROW(disk.EvaluateBlockDescriptors(scsi_command::cmd_mode_select10, buf, sector_size)); EXPECT_EQ(1024, sector_size); + + buf = CreateParameters("00:00:00:00:00:00:00:08:00:08:00:00:00:00:03:fc"); + EXPECT_NO_THROW(disk.EvaluateBlockDescriptors(scsi_command::cmd_mode_select10, buf, sector_size)); + EXPECT_EQ(1020, sector_size); } TEST(DiskTest, VerifySectorSizeChange) @@ -710,9 +715,8 @@ TEST(DiskTest, VerifySectorSizeChange) TEST(DiskTest, ReadData) { MockDisk disk; - vector buf; - EXPECT_THAT([&] {disk.ReadData(buf);}, Throws(AllOf( + EXPECT_THAT([&] {disk.ReadData( {});}, Throws(AllOf( Property(&scsi_exception::get_sense_key, sense_key::not_ready), Property(&scsi_exception::get_asc, asc::medium_not_present)))) << "Disk is not ready"; } @@ -720,9 +724,8 @@ TEST(DiskTest, ReadData) TEST(DiskTest, WriteData) { MockDisk disk; - vector buf; - EXPECT_THAT([&] {disk.WriteData(buf, scsi_command::cmd_write6);}, Throws(AllOf( + EXPECT_THAT([&] {disk.WriteData( {}, scsi_command::cmd_write6);}, Throws(AllOf( Property(&scsi_exception::get_sense_key, sense_key::not_ready), Property(&scsi_exception::get_asc, asc::medium_not_present)))) << "Disk is not ready"; } @@ -790,7 +793,6 @@ TEST(DiskTest, ChangeSectorSize) EXPECT_EQ(1024U, disk.GetSectorSizeInBytes()); disk.SetBlockCount(10); - disk.SetUpCache(); EXPECT_CALL(disk, FlushCache()); disk.ChangeSectorSize(512); EXPECT_EQ(512U, disk.GetSectorSizeInBytes()); diff --git a/cpp/test/host_services_test.cpp b/cpp/test/host_services_test.cpp index edeacad1..01803d42 100644 --- a/cpp/test/host_services_test.cpp +++ b/cpp/test/host_services_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -8,28 +8,27 @@ #include "mocks.h" #include "shared/shared_exceptions.h" -#include "base/device_factory.h" TEST(HostServicesTest, DeviceDefaults) { - auto device = DeviceFactory::Instance().CreateDevice(UNDEFINED, 0, "services"); - EXPECT_NE(nullptr, device); - EXPECT_EQ(SCHS, device->GetType()); - EXPECT_FALSE(device->SupportsFile()); - EXPECT_FALSE(device->SupportsParams()); - EXPECT_FALSE(device->IsProtectable()); - EXPECT_FALSE(device->IsProtected()); - EXPECT_FALSE(device->IsReadOnly()); - EXPECT_FALSE(device->IsRemovable()); - EXPECT_FALSE(device->IsRemoved()); - EXPECT_FALSE(device->IsLockable()); - EXPECT_FALSE(device->IsLocked()); - EXPECT_FALSE(device->IsStoppable()); - EXPECT_FALSE(device->IsStopped()); - - EXPECT_EQ("SCSI2Pi", device->GetVendor()); - EXPECT_EQ("Host Services", device->GetProduct()); - EXPECT_EQ(TestShared::GetVersion(), device->GetRevision()); + HostServices services(0); + + EXPECT_EQ(SCHS, services.GetType()); + EXPECT_FALSE(services.SupportsFile()); + EXPECT_FALSE(services.SupportsParams()); + EXPECT_FALSE(services.IsProtectable()); + EXPECT_FALSE(services.IsProtected()); + EXPECT_FALSE(services.IsReadOnly()); + EXPECT_FALSE(services.IsRemovable()); + EXPECT_FALSE(services.IsRemoved()); + EXPECT_FALSE(services.IsLockable()); + EXPECT_FALSE(services.IsLocked()); + EXPECT_FALSE(services.IsStoppable()); + EXPECT_FALSE(services.IsStopped()); + + EXPECT_EQ("SCSI2Pi", services.GetVendor()); + EXPECT_EQ("Host Services", services.GetProduct()); + EXPECT_EQ(TestShared::GetVersion(), services.GetRevision()); } void HostServices_SetUpModePages(map> &pages) diff --git a/cpp/test/image_support_test.cpp b/cpp/test/image_support_test.cpp index 0a50e8c9..128d4a9b 100644 --- a/cpp/test/image_support_test.cpp +++ b/cpp/test/image_support_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // diff --git a/cpp/test/in_process_bus_test.cpp b/cpp/test/in_process_bus_test.cpp index 37f78517..c09d796e 100644 --- a/cpp/test/in_process_bus_test.cpp +++ b/cpp/test/in_process_bus_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2023-2024 Uwe Seimet // @@ -183,26 +183,36 @@ TEST(DelegatingProcessBusTest, Acquire) EXPECT_EQ(0x45U, delegating_bus.Acquire()); } -TEST(DelegatingProcessBusTest, WaitACK) +TEST(DelegatingProcessBusTest, SetGetSignal) { MockInProcessBus bus; - DelegatingInProcessBus delegating_bus(bus, false); + DelegatingInProcessBus delegating_bus(bus, true); - bus.SetACK(true); - EXPECT_TRUE(delegating_bus.WaitACK(true)); - bus.SetACK(false); - EXPECT_TRUE(delegating_bus.WaitACK(false)); + delegating_bus.SetSignal(PIN_ACK, true); + EXPECT_TRUE(delegating_bus.GetSignal(PIN_ACK)); + delegating_bus.SetSignal(PIN_ACK, false); + EXPECT_FALSE(delegating_bus.GetSignal(PIN_ACK)); + + delegating_bus.SetSignal(PIN_IO, true); + EXPECT_TRUE(delegating_bus.GetSignal(PIN_IO)); + delegating_bus.SetSignal(PIN_IO, false); + EXPECT_FALSE(delegating_bus.GetSignal(PIN_IO)); } -TEST(DelegatingProcessBusTest, WaitREQ) +TEST(DelegatingProcessBusTest, WaitSignal) { MockInProcessBus bus; DelegatingInProcessBus delegating_bus(bus, false); + bus.SetACK(true); + EXPECT_TRUE(delegating_bus.WaitSignal(PIN_ACK, true)); + bus.SetACK(false); + EXPECT_TRUE(delegating_bus.WaitSignal(PIN_ACK, false)); + bus.SetREQ(true); - EXPECT_TRUE(delegating_bus.WaitREQ(true)); + EXPECT_TRUE(delegating_bus.WaitSignal(PIN_REQ, true)); bus.SetREQ(false); - EXPECT_TRUE(delegating_bus.WaitREQ(false)); + EXPECT_TRUE(delegating_bus.WaitSignal(PIN_REQ, false)); } TEST(DelegatingProcessBusTest, DAT) diff --git a/cpp/test/in_process_test.cpp b/cpp/test/in_process_test.cpp index 0b090d62..188d1dfb 100644 --- a/cpp/test/in_process_test.cpp +++ b/cpp/test/in_process_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2023-2024 Uwe Seimet // diff --git a/cpp/test/initiator_util_test.cpp b/cpp/test/initiator_util_test.cpp new file mode 100644 index 00000000..ca9b46d0 --- /dev/null +++ b/cpp/test/initiator_util_test.cpp @@ -0,0 +1,20 @@ +//--------------------------------------------------------------------------- +// +// SCSI device emulator and SCSI tools for the Raspberry Pi +// +// Copyright (C) 2024 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#include "mocks.h" +#include "initiator/initiator_util.h" + +using namespace initiator_util; + +TEST(InitiatorUtilTest, ResetBus) +{ + NiceMock bus; + + EXPECT_CALL(bus, Reset); + ResetBus(bus); +} diff --git a/cpp/test/linux_cache_test.cpp b/cpp/test/linux_cache_test.cpp index 257b91ba..325534b7 100644 --- a/cpp/test/linux_cache_test.cpp +++ b/cpp/test/linux_cache_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2024 Uwe Seimet // diff --git a/cpp/test/localizer_test.cpp b/cpp/test/localizer_test.cpp index 25210d10..ecbb83d5 100644 --- a/cpp/test/localizer_test.cpp +++ b/cpp/test/localizer_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022 Uwe Seimet // diff --git a/cpp/test/memory_util_test.cpp b/cpp/test/memory_util_test.cpp index 71d9cdb2..8578426c 100644 --- a/cpp/test/memory_util_test.cpp +++ b/cpp/test/memory_util_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2023 Uwe Seimet // diff --git a/cpp/test/mocks.h b/cpp/test/mocks.h index 16e87130..aee21443 100644 --- a/cpp/test/mocks.h +++ b/cpp/test/mocks.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -11,7 +11,7 @@ #include #include "command/command_executor.h" #include "buses/in_process_bus.h" -#include "controllers/scsi_controller.h" +#include "controllers/controller.h" #include "devices/sasi_hd.h" #include "devices/scsi_hd.h" #include "devices/scsi_cd.h" @@ -21,7 +21,7 @@ using namespace testing; -class MockBus : public Bus // NOSONAR Having many fields/methods cannot be avoided +class MockBus : public Bus // NOSONAR Having many methods cannot be avoided { public: @@ -29,43 +29,20 @@ class MockBus : public Bus // NOSONAR Having many fields/methods cannot be avoid MOCK_METHOD(bool, Init, (bool), (override)); MOCK_METHOD(void, Reset, (), (override)); MOCK_METHOD(void, CleanUp, (), (override)); - MOCK_METHOD(bool, GetBSY, (), (const, override)); MOCK_METHOD(void, SetBSY, (bool), (override)); - MOCK_METHOD(bool, GetSEL, (), (const, override)); MOCK_METHOD(void, SetSEL, (bool), (override)); - MOCK_METHOD(bool, GetATN, (), (const, override)); - MOCK_METHOD(void, SetATN, (bool), (override)); - MOCK_METHOD(bool, GetACK, (), (const, override)); - MOCK_METHOD(void, SetACK, (bool), (override)); - MOCK_METHOD(bool, GetRST, (), (const, override)); - MOCK_METHOD(void, SetRST, (bool), (override)); - MOCK_METHOD(bool, GetMSG, (), (const, override)); - MOCK_METHOD(void, SetMSG, (bool), (override)); - MOCK_METHOD(bool, GetCD, (), (const, override)); - MOCK_METHOD(void, SetCD, (bool), (override)); MOCK_METHOD(bool, GetIO, (), (override)); MOCK_METHOD(void, SetIO, (bool), (override)); - MOCK_METHOD(bool, GetREQ, (), (const, override)); - MOCK_METHOD(void, SetREQ, (bool), (override)); MOCK_METHOD(uint8_t, GetDAT, (), (override)); MOCK_METHOD(void, SetDAT, (uint8_t), (override)); MOCK_METHOD(uint32_t, Acquire, (), (override)); - MOCK_METHOD(int, CommandHandShake, (vector&), (override)); - MOCK_METHOD(int, MsgInHandShake, (), (override)); - MOCK_METHOD(int, ReceiveHandShake, (uint8_t *, int), (override)); - MOCK_METHOD(int, SendHandShake, (uint8_t *, int, int), (override)); MOCK_METHOD(bool, GetSignal, (int), (const, override)); MOCK_METHOD(void, SetSignal, (int, bool), (override)); - MOCK_METHOD(bool, WaitREQ, (bool), (override)); - MOCK_METHOD(bool, WaitACK, (bool), (override)); + MOCK_METHOD(bool, WaitSignal, (int, bool), (override)); MOCK_METHOD(bool, WaitForSelection, (), (override)); - MOCK_METHOD(void, PinConfig, (int, int), (override)); - MOCK_METHOD(void, PullConfig, (int, int), (override)); - MOCK_METHOD(void, SetControl, (int, bool), (override)); - MOCK_METHOD(void, SetMode, (int, int), (override)); - - MockBus() = default; - ~MockBus() override = default; + MOCK_METHOD(void, WaitBusSettle, (), (const, override)); + MOCK_METHOD(void, EnableIRQ, (), (override)); + MOCK_METHOD(void, DisableIRQ, (), (override)); }; class MockInProcessBus : public InProcessBus @@ -100,30 +77,30 @@ class MockPhaseHandler : public PhaseHandler MOCK_METHOD(void, Command, (), (override)); MOCK_METHOD(void, MsgIn, (), (override)); MOCK_METHOD(void, MsgOut, (), (override)); - MOCK_METHOD(bool, Process, (int), (override)); using PhaseHandler::PhaseHandler; }; inline static const auto mock_bus = make_shared(); -class MockAbstractController : public AbstractController // NOSONAR Having many fields/methods cannot be avoided +class MockAbstractController : public AbstractController // NOSONAR Having many methods cannot be avoided { friend class testing::TestShared; friend shared_ptr CreateDevice(s2p_interface::PbDeviceType, AbstractController&, int); FRIEND_TEST(AbstractControllerTest, Reset); + FRIEND_TEST(AbstractControllerTest, Status); FRIEND_TEST(AbstractControllerTest, DeviceLunLifeCycle); FRIEND_TEST(AbstractControllerTest, ExtractInitiatorId); FRIEND_TEST(AbstractControllerTest, GetOpcode); - FRIEND_TEST(AbstractControllerTest, GetLun); FRIEND_TEST(AbstractControllerTest, Message); FRIEND_TEST(AbstractControllerTest, TransferSize); FRIEND_TEST(AbstractControllerTest, Length); FRIEND_TEST(AbstractControllerTest, UpdateOffsetAndLength); FRIEND_TEST(AbstractControllerTest, Offset); - FRIEND_TEST(ScsiControllerTest, Selection); + FRIEND_TEST(ControllerTest, Selection); + FRIEND_TEST(PrimaryDeviceTest, CheckReservation); FRIEND_TEST(PrimaryDeviceTest, Inquiry); FRIEND_TEST(PrimaryDeviceTest, TestUnitReady); FRIEND_TEST(PrimaryDeviceTest, RequestSense); @@ -183,10 +160,9 @@ class MockAbstractController : public AbstractController // NOSONAR Having many public: - MOCK_METHOD(bool, Process, (int), (override)); + MOCK_METHOD(bool, Process, (), (override)); MOCK_METHOD(int, GetEffectiveLun, (), (const, override)); MOCK_METHOD(void, Error, (scsi_defs::sense_key, scsi_defs::asc, scsi_defs::status), (override)); - MOCK_METHOD(int, GetInitiatorId, (), (const, override)); MOCK_METHOD(void, Status, (), (override)); MOCK_METHOD(void, DataIn, (), (override)); MOCK_METHOD(void, DataOut, (), (override)); @@ -210,18 +186,18 @@ class MockAbstractController : public AbstractController // NOSONAR Having many ~MockAbstractController() override = default; }; -class MockScsiController : public ScsiController +class MockController : public Controller { - FRIEND_TEST(ScsiControllerTest, Process); - FRIEND_TEST(ScsiControllerTest, BusFree); - FRIEND_TEST(ScsiControllerTest, Selection); - FRIEND_TEST(ScsiControllerTest, Command); - FRIEND_TEST(ScsiControllerTest, MsgIn); - FRIEND_TEST(ScsiControllerTest, MsgOut); - FRIEND_TEST(ScsiControllerTest, DataIn); - FRIEND_TEST(ScsiControllerTest, DataOut); - FRIEND_TEST(ScsiControllerTest, Error); - FRIEND_TEST(ScsiControllerTest, RequestSense); + FRIEND_TEST(ControllerTest, Process); + FRIEND_TEST(ControllerTest, BusFree); + FRIEND_TEST(ControllerTest, Selection); + FRIEND_TEST(ControllerTest, Command); + FRIEND_TEST(ControllerTest, MsgIn); + FRIEND_TEST(ControllerTest, MsgOut); + FRIEND_TEST(ControllerTest, DataIn); + FRIEND_TEST(ControllerTest, DataOut); + FRIEND_TEST(ControllerTest, Error); + FRIEND_TEST(ControllerTest, RequestSense); FRIEND_TEST(PrimaryDeviceTest, RequestSense); public: @@ -230,14 +206,14 @@ class MockScsiController : public ScsiController MOCK_METHOD(void, Status, (), (override)); MOCK_METHOD(void, Execute, (), ()); - using ScsiController::ScsiController; - MockScsiController(shared_ptr bus, int target_id) : ScsiController(*bus, target_id, 32) + using Controller::Controller; + MockController(shared_ptr bus, int target_id) : Controller(*bus, target_id, 32) { } - explicit MockScsiController(shared_ptr bus) : ScsiController(*bus, 0, 32) + explicit MockController(shared_ptr bus) : Controller(*bus, 0, 32) { } - ~MockScsiController() override = default; + ~MockController() override = default; }; class MockDevice : public Device @@ -271,7 +247,7 @@ class MockPrimaryDevice : public PrimaryDevice FRIEND_TEST(PrimaryDeviceTest, TestUnitReady); FRIEND_TEST(PrimaryDeviceTest, RequestSense); FRIEND_TEST(PrimaryDeviceTest, Inquiry); - FRIEND_TEST(ScsiControllerTest, RequestSense); + FRIEND_TEST(ControllerTest, RequestSense); FRIEND_TEST(CommandExecutorTest, ValidateOperationAgainstDevice); public: @@ -297,7 +273,7 @@ class MockModePageDevice : public ModePageDevice MOCK_METHOD(int, ModeSense6, (span, vector&), (const, override)); MOCK_METHOD(int, ModeSense10, (span, vector&), (const, override)); - MockModePageDevice() : ModePageDevice(UNDEFINED, scsi_level::scsi_2, 0, false) + MockModePageDevice() : ModePageDevice(UNDEFINED, scsi_level::scsi_2, 0, false, false) { } ~MockModePageDevice() override = default; @@ -312,23 +288,6 @@ class MockModePageDevice : public ModePageDevice } }; -class MockPage0ModePageDevice : public MockModePageDevice -{ - FRIEND_TEST(ModePageDeviceTest, Page0); - -public: - - using MockModePageDevice::MockModePageDevice; - - void SetUpModePages(map> &pages, int, bool) const override - { - // Return dummy data for pages 0 and 1 - vector buf(32); - pages[0] = buf; - pages[1] = buf; - } -}; - class MockStorageDevice : public StorageDevice { FRIEND_TEST(StorageDeviceTest, ValidateFile); @@ -345,7 +304,7 @@ class MockStorageDevice : public StorageDevice MOCK_METHOD(int, ModeSense10, (span, vector&), (const, override)); MOCK_METHOD(void, SetUpModePages, ((map>&), int, bool), (const, override)); - MockStorageDevice() : StorageDevice(UNDEFINED, scsi_level::scsi_2, 0, false) + MockStorageDevice() : StorageDevice(UNDEFINED, scsi_level::scsi_2, 0, false, false) { } ~MockStorageDevice() override = default; @@ -353,7 +312,6 @@ class MockStorageDevice : public StorageDevice class MockDisk : public Disk { - FRIEND_TEST(DiskTest, SetUpCache); FRIEND_TEST(DiskTest, Dispatch); FRIEND_TEST(DiskTest, Rezero); FRIEND_TEST(DiskTest, FormatUnit); @@ -396,7 +354,7 @@ class MockDisk : public Disk MOCK_METHOD(void, FlushCache, (), (override)); MOCK_METHOD(void, Open, (), (override)); - MockDisk() : Disk(SCHD, scsi_level::scsi_2, 0, false, { 512, 1024, 2048, 4096 }) + MockDisk() : Disk(SCHD, scsi_level::scsi_2, 0, false, false, { 512, 1024, 2048, 4096 }) { SetCachingMode(PbCachingMode::PISCSI); } @@ -405,8 +363,6 @@ class MockDisk : public Disk class MockSasiHd : public SasiHd // NOSONAR Ignore inheritance hierarchy depth in unit tests { - FRIEND_TEST(SasiHdTest, FinalizeSetup); - public: explicit MockSasiHd(int lun) : SasiHd(lun) diff --git a/cpp/test/mode_page_device_test.cpp b/cpp/test/mode_page_device_test.cpp index 566751e3..c50338a7 100644 --- a/cpp/test/mode_page_device_test.cpp +++ b/cpp/test/mode_page_device_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -10,17 +10,6 @@ #include "shared/shared_exceptions.h" #include "devices/mode_page_device.h" -TEST(ModePageDeviceTest, SupportsSaveParameters) -{ - MockModePageDevice device; - - EXPECT_FALSE(device.SupportsSaveParameters()) << "Wrong default value"; - device.SupportsSaveParameters(true); - EXPECT_TRUE(device.SupportsSaveParameters()); - device.SupportsSaveParameters(false); - EXPECT_FALSE(device.SupportsSaveParameters()); -} - TEST(ModePageDeviceTest, AddModePages) { vector buf(512); @@ -50,16 +39,6 @@ TEST(ModePageDeviceTest, AddModePages) Property(&scsi_exception::get_asc, asc::invalid_field_in_cdb)))) << "Maximum size was ignored"; } -TEST(ModePageDeviceTest, Page0) -{ - vector buf(512); - MockPage0ModePageDevice device; - - const vector cdb = CreateCdb(scsi_command::cmd_mode_select6, "00:3f:00:00:00"); - EXPECT_EQ(0, device.AddModePages(cdb, buf, 0, 0, 255)); - EXPECT_EQ(1, device.AddModePages(cdb, buf, 0, 1, 255)); -} - TEST(ModePageDeviceTest, AddVendorPages) { map> pages; diff --git a/cpp/test/network_util_test.cpp b/cpp/test/network_util_test.cpp index 10727e91..5bc15e30 100644 --- a/cpp/test/network_util_test.cpp +++ b/cpp/test/network_util_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2023 Uwe Seimet // diff --git a/cpp/test/optical_memory_test.cpp b/cpp/test/optical_memory_test.cpp index e26f81ef..78c2c641 100644 --- a/cpp/test/optical_memory_test.cpp +++ b/cpp/test/optical_memory_test.cpp @@ -1,12 +1,13 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // //--------------------------------------------------------------------------- #include "mocks.h" +#include "shared/shared_exceptions.h" #include "base/memory_util.h" using namespace scsi_defs; @@ -29,13 +30,6 @@ TEST(OpticalMemoryTest, Inquiry) TestShared::Inquiry(SCMO, device_type::optical_memory, scsi_level::scsi_2, "SCSI2Pi SCSI MO ", 0x1f, true); } -TEST(OpticalMemoryTest, SupportsSaveParameters) -{ - MockOpticalMemory mo(0); - - EXPECT_TRUE(mo.SupportsSaveParameters()); -} - TEST(OpticalMemoryTest, GetSectorSizes) { MockOpticalMemory mo(0); diff --git a/cpp/test/phase_handler_test.cpp b/cpp/test/phase_handler_test.cpp index 7ee82a0f..c39dc98d 100644 --- a/cpp/test/phase_handler_test.cpp +++ b/cpp/test/phase_handler_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2023 Uwe Seimet // @@ -131,6 +131,9 @@ TEST(PhaseHandlerTest, ProcessPhase) EXPECT_CALL(handler, MsgOut); EXPECT_TRUE(handler.ProcessPhase()); + handler.SetPhase(phase_t::arbitration); + EXPECT_FALSE(handler.ProcessPhase()); + handler.SetPhase(phase_t::reselection); EXPECT_FALSE(handler.ProcessPhase()); diff --git a/cpp/test/primary_device_test.cpp b/cpp/test/primary_device_test.cpp index 2e302020..77813942 100644 --- a/cpp/test/primary_device_test.cpp +++ b/cpp/test/primary_device_test.cpp @@ -1,13 +1,12 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // //--------------------------------------------------------------------------- #include "mocks.h" -#include "shared/scsi.h" #include "shared/shared_exceptions.h" #include "base/device_factory.h" #include "base/memory_util.h" @@ -27,15 +26,15 @@ pair, shared_ptr> CreatePr TEST(PrimaryDeviceTest, SetScsiLevel) { - auto device = make_shared(0); + MockPrimaryDevice device(0); - EXPECT_EQ(scsi_level::scsi_2, device->GetScsiLevel()); + EXPECT_EQ(scsi_level::scsi_2, device.GetScsiLevel()); - EXPECT_FALSE(device->SetScsiLevel(scsi_level::none)); - EXPECT_FALSE(device->SetScsiLevel(static_cast(9))); + EXPECT_FALSE(device.SetScsiLevel(scsi_level::none)); + EXPECT_FALSE(device.SetScsiLevel(static_cast(9))); - EXPECT_TRUE(device->SetScsiLevel(scsi_level::spc_6)); - EXPECT_EQ(scsi_level::spc_6, device->GetScsiLevel()); + EXPECT_TRUE(device.SetScsiLevel(scsi_level::spc_6)); + EXPECT_EQ(scsi_level::spc_6, device.GetScsiLevel()); } TEST(PrimaryDeviceTest, Status) @@ -51,7 +50,7 @@ TEST(PrimaryDeviceTest, GetId) { const int ID = 5; - auto [controller, device] = CreatePrimaryDevice(ID); + auto [_, device] = CreatePrimaryDevice(ID); EXPECT_EQ(ID, device->GetId()); } @@ -87,37 +86,34 @@ TEST(PrimaryDeviceTest, Reset) auto [controller, device] = CreatePrimaryDevice(); EXPECT_NO_THROW(device->Dispatch(scsi_command::cmd_reserve6)); - EXPECT_FALSE(device->CheckReservation(1, scsi_command::cmd_test_unit_ready, false)) - << "Device must be reserved for initiator ID 1"; + EXPECT_FALSE(device->CheckReservation(1)) << "Device must be reserved for initiator ID 1"; device->Reset(); - EXPECT_TRUE(device->CheckReservation(1, scsi_command::cmd_test_unit_ready, false)) - << "Device must not be reserved anymore for initiator ID 1"; + EXPECT_TRUE(device->CheckReservation(1)) << "Device must not be reserved anymore for initiator ID 1"; } TEST(PrimaryDeviceTest, CheckReservation) { auto [controller, device] = CreatePrimaryDevice(); - EXPECT_TRUE(device->CheckReservation(0, scsi_command::cmd_test_unit_ready, false)) - << "Device must not be reserved for initiator ID 0"; + EXPECT_TRUE(device->CheckReservation(0)) << "Device must not be reserved for initiator ID 0"; + controller->ProcessOnController(0); EXPECT_NO_THROW(device->Dispatch(scsi_command::cmd_reserve6)); - EXPECT_TRUE(device->CheckReservation(0, scsi_command::cmd_test_unit_ready, false)) - << "Device must not be reserved for initiator ID 0"; - EXPECT_FALSE(device->CheckReservation(1, scsi_command::cmd_test_unit_ready, false)) - << "Device must be reserved for initiator ID 1"; - EXPECT_FALSE(device->CheckReservation(-1, scsi_command::cmd_test_unit_ready, false)) - << "Device must be reserved for unknown initiator"; - EXPECT_TRUE(device->CheckReservation(1, scsi_command::cmd_inquiry, false)) - << "Device must not be reserved for INQUIRY"; - EXPECT_TRUE(device->CheckReservation(1, scsi_command::cmd_request_sense, false)) - << "Device must not be reserved for REQUEST SENSE"; - EXPECT_TRUE(device->CheckReservation(1, scsi_command::cmd_release6, false)) - << "Device must not be reserved for RELEASE (6)"; - - EXPECT_TRUE(device->CheckReservation(1, scsi_command::cmd_prevent_allow_medium_removal, false)) + EXPECT_TRUE(device->CheckReservation(0)) << "Device must not be reserved for initiator ID 0"; + EXPECT_FALSE(device->CheckReservation(1)) << "Device must be reserved for initiator ID 1"; + EXPECT_FALSE(device->CheckReservation(-1)) << "Device must be reserved for unknown initiator"; + controller->SetCdbByte(0, static_cast(scsi_command::cmd_inquiry)); + EXPECT_TRUE(device->CheckReservation(1)) << "Device must not be reserved for INQUIRY"; + controller->SetCdbByte(0, static_cast(scsi_command::cmd_request_sense)); + EXPECT_TRUE(device->CheckReservation(1)) << "Device must not be reserved for REQUEST SENSE"; + controller->SetCdbByte(0, static_cast(scsi_command::cmd_release6)); + EXPECT_TRUE(device->CheckReservation(1)) << "Device must not be reserved for RELEASE (6)"; + + controller->SetCdbByte(0, static_cast(scsi_command::cmd_prevent_allow_medium_removal)); + EXPECT_TRUE(device->CheckReservation(1)) << "Device must not be reserved for PREVENT ALLOW MEDIUM REMOVAL with prevent bit not set"; - EXPECT_FALSE(device->CheckReservation(1, scsi_command::cmd_prevent_allow_medium_removal, true)) + controller->SetCdbByte(4, 0x01); + EXPECT_FALSE(device->CheckReservation(1)) << "Device must be reserved for PREVENT ALLOW MEDIUM REMOVAL with prevent bit set"; } @@ -126,21 +122,16 @@ TEST(PrimaryDeviceTest, ReserveReleaseUnit) auto [controller, device] = CreatePrimaryDevice(); EXPECT_NO_THROW(device->Dispatch(scsi_command::cmd_reserve6)); - EXPECT_FALSE(device->CheckReservation(1, scsi_command::cmd_test_unit_ready, false)) - << "Device must be reserved for initiator ID 1"; + EXPECT_FALSE(device->CheckReservation(1)) << "Device must be reserved for initiator ID 1"; EXPECT_NO_THROW(device->Dispatch(scsi_command::cmd_release6)); - EXPECT_TRUE(device->CheckReservation(1, scsi_command::cmd_test_unit_ready, false)) - << "Device must not be reserved anymore for initiator ID 1"; + EXPECT_TRUE(device->CheckReservation(1)) << "Device must not be reserved anymore for initiator ID 1"; - ON_CALL(*controller, GetInitiatorId).WillByDefault(Return(-1)); EXPECT_NO_THROW(device->Dispatch(scsi_command::cmd_reserve6)); - EXPECT_FALSE(device->CheckReservation(1, scsi_command::cmd_test_unit_ready, false)) - << "Device must be reserved for unknown initiator"; + EXPECT_FALSE(device->CheckReservation(1)) << "Device must be reserved for unknown initiator"; EXPECT_NO_THROW(device->Dispatch(scsi_command::cmd_release6)); - EXPECT_TRUE(device->CheckReservation(1, scsi_command::cmd_test_unit_ready, false)) - << "Device must not be reserved anymore for unknown initiator"; + EXPECT_TRUE(device->CheckReservation(1)) << "Device must not be reserved anymore for unknown initiator"; } TEST(PrimaryDeviceTest, DiscardReservation) @@ -148,11 +139,9 @@ TEST(PrimaryDeviceTest, DiscardReservation) auto [controller, device] = CreatePrimaryDevice(); EXPECT_NO_THROW(device->Dispatch(scsi_command::cmd_reserve6)); - EXPECT_FALSE(device->CheckReservation(1, scsi_command::cmd_test_unit_ready, false)) - << "Device must be reserved for initiator ID 1"; + EXPECT_FALSE(device->CheckReservation(1)) << "Device must be reserved for initiator ID 1"; EXPECT_NO_THROW(device->DiscardReservation()); - EXPECT_TRUE(device->CheckReservation(1, scsi_command::cmd_test_unit_ready, false)) - << "Device must not be reserved anymore for initiator ID 1"; + EXPECT_TRUE(device->CheckReservation(1)) << "Device must not be reserved anymore for initiator ID 1"; } TEST(PrimaryDeviceTest, TestUnitReady) @@ -328,18 +317,17 @@ TEST(PrimaryDeviceTest, ReportLuns) TEST(PrimaryDeviceTest, Dispatch) { - auto [controller, device] = CreatePrimaryDevice(); + MockPrimaryDevice device(0); - TestShared::Dispatch(*device, static_cast(0x1f), sense_key::illegal_request, + TestShared::Dispatch(device, static_cast(0x1f), sense_key::illegal_request, asc::invalid_command_operation_code, "Unsupported SCSI command"); } TEST(PrimaryDeviceTest, Init) { - param_map params; MockPrimaryDevice device(0); - EXPECT_TRUE(device.Init(params)) << "Initialization of primary device must not fail"; + EXPECT_TRUE(device.Init( {})) << "Initialization of primary device must not fail"; } TEST(PrimaryDeviceTest, GetDelayAfterBytes) diff --git a/cpp/test/printer_test.cpp b/cpp/test/printer_test.cpp index 754f5589..0630eeb3 100644 --- a/cpp/test/printer_test.cpp +++ b/cpp/test/printer_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -8,29 +8,28 @@ #include "mocks.h" #include "shared/shared_exceptions.h" -#include "base/device_factory.h" #include "devices/printer.h" TEST(PrinterTest, Device_Defaults) { - auto device = DeviceFactory::Instance().CreateDevice(UNDEFINED, 0, "printer"); - EXPECT_NE(nullptr, device); - EXPECT_EQ(SCLP, device->GetType()); - EXPECT_FALSE(device->SupportsFile()); - EXPECT_TRUE(device->SupportsParams()); - EXPECT_FALSE(device->IsProtectable()); - EXPECT_FALSE(device->IsProtected()); - EXPECT_FALSE(device->IsReadOnly()); - EXPECT_FALSE(device->IsRemovable()); - EXPECT_FALSE(device->IsRemoved()); - EXPECT_FALSE(device->IsLockable()); - EXPECT_FALSE(device->IsLocked()); - EXPECT_FALSE(device->IsStoppable()); - EXPECT_FALSE(device->IsStopped()); - - EXPECT_EQ("SCSI2Pi", device->GetVendor()); - EXPECT_EQ("SCSI PRINTER", device->GetProduct()); - EXPECT_EQ(TestShared::GetVersion(), device->GetRevision()); + Printer printer(0); + + EXPECT_EQ(SCLP, printer.GetType()); + EXPECT_FALSE(printer.SupportsFile()); + EXPECT_TRUE(printer.SupportsParams()); + EXPECT_FALSE(printer.IsProtectable()); + EXPECT_FALSE(printer.IsProtected()); + EXPECT_FALSE(printer.IsReadOnly()); + EXPECT_FALSE(printer.IsRemovable()); + EXPECT_FALSE(printer.IsRemoved()); + EXPECT_FALSE(printer.IsLockable()); + EXPECT_FALSE(printer.IsLocked()); + EXPECT_FALSE(printer.IsStoppable()); + EXPECT_FALSE(printer.IsStopped()); + + EXPECT_EQ("SCSI2Pi", printer.GetVendor()); + EXPECT_EQ("SCSI PRINTER", printer.GetProduct()); + EXPECT_EQ(TestShared::GetVersion(), printer.GetRevision()); } TEST(PrinterTest, GetDefaultParams) diff --git a/cpp/test/property_handler_test.cpp b/cpp/test/property_handler_test.cpp index 7db77eff..0b6e644e 100644 --- a/cpp/test/property_handler_test.cpp +++ b/cpp/test/property_handler_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2024 Uwe Seimet // @@ -10,6 +10,7 @@ #include #include "test/test_shared.h" +#include "shared/shared_exceptions.h" #include "base/property_handler.h" using namespace testing; diff --git a/cpp/test/protobuf_util_test.cpp b/cpp/test/protobuf_util_test.cpp index 721294e7..8a43f6d52 100644 --- a/cpp/test/protobuf_util_test.cpp +++ b/cpp/test/protobuf_util_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // diff --git a/cpp/test/s2p_parser_test.cpp b/cpp/test/s2p_parser_test.cpp index 111fc493..93c20263 100644 --- a/cpp/test/s2p_parser_test.cpp +++ b/cpp/test/s2p_parser_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2024 Uwe Seimet // diff --git a/cpp/test/s2p_thread_test.cpp b/cpp/test/s2p_thread_test.cpp index d5d48362..2e6ae9ca 100644 --- a/cpp/test/s2p_thread_test.cpp +++ b/cpp/test/s2p_thread_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2023 Uwe Seimet // diff --git a/cpp/test/s2p_util_test.cpp b/cpp/test/s2p_util_test.cpp index b66a0b28..0eeaec8e 100644 --- a/cpp/test/s2p_util_test.cpp +++ b/cpp/test/s2p_util_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -174,12 +174,12 @@ TEST(S2pUtilTest, GetHexBytes) bytes = HexToBytes("ab:cd\n12"); EXPECT_EQ(result, bytes); - EXPECT_THROW(HexToBytes("ab:cd12xx"), parser_exception); - EXPECT_THROW(HexToBytes(":abcd12"), parser_exception); - EXPECT_THROW(HexToBytes("abcd12:"), parser_exception); - EXPECT_THROW(HexToBytes("ab::cd12"), parser_exception); - EXPECT_THROW(HexToBytes("9"), parser_exception); - EXPECT_THROW(HexToBytes("012"), parser_exception); + EXPECT_THROW(HexToBytes("ab:cd12xx"), out_of_range); + EXPECT_THROW(HexToBytes(":abcd12"), out_of_range); + EXPECT_THROW(HexToBytes("abcd12:"), out_of_range); + EXPECT_THROW(HexToBytes("ab::cd12"), out_of_range); + EXPECT_THROW(HexToBytes("9"), out_of_range); + EXPECT_THROW(HexToBytes("012"), out_of_range); } TEST(S2pUtilTest, FormatBytes) diff --git a/cpp/test/s2pctl_commands_test.cpp b/cpp/test/s2pctl_commands_test.cpp index afcf61a1..ccb5cea4 100644 --- a/cpp/test/s2pctl_commands_test.cpp +++ b/cpp/test/s2pctl_commands_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // diff --git a/cpp/test/s2pctl_display_test.cpp b/cpp/test/s2pctl_display_test.cpp index 680ae8a5..5847a0d2 100644 --- a/cpp/test/s2pctl_display_test.cpp +++ b/cpp/test/s2pctl_display_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // diff --git a/cpp/test/sasi_hd_test.cpp b/cpp/test/sasi_hd_test.cpp index 08e5e961..5387fb4a 100644 --- a/cpp/test/sasi_hd_test.cpp +++ b/cpp/test/sasi_hd_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2023-2024 Uwe Seimet // @@ -28,7 +28,7 @@ TEST(SasiHdTest, RequestSense) auto [controller, hd] = CreateDevice(SAHD, LUN); // ALLOCATION LENGTH - controller->SetCdbByte(4, 255); + controller->SetCdbByte(4, 4); EXPECT_CALL(*controller, DataIn()); EXPECT_NO_THROW(hd->Dispatch(scsi_command::cmd_request_sense)); span buffer = controller->GetBuffer(); @@ -36,21 +36,12 @@ TEST(SasiHdTest, RequestSense) EXPECT_EQ(LUN << 5, buffer[1]); } -TEST(SasiHdTest, FinalizeSetup) -{ - MockSasiHd hd(0); - - hd.SetSectorSizeInBytes(1024); - EXPECT_THROW(hd.FinalizeSetup(), io_exception)<< "Device has 0 blocks"; -} - TEST(SasiHdTest, GetSectorSizes) { MockSasiHd hd(0); const auto §or_sizes = hd.GetSupportedSectorSizes(); EXPECT_EQ(3U, sector_sizes.size()); - EXPECT_TRUE(sector_sizes.contains(256)); EXPECT_TRUE(sector_sizes.contains(512)); EXPECT_TRUE(sector_sizes.contains(1024)); diff --git a/cpp/test/scsi_cd_test.cpp b/cpp/test/scsi_cd_test.cpp index 375189bc..4f706aac 100644 --- a/cpp/test/scsi_cd_test.cpp +++ b/cpp/test/scsi_cd_test.cpp @@ -1,36 +1,34 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // //--------------------------------------------------------------------------- -#include #include "mocks.h" #include "shared/shared_exceptions.h" -#include "base/device_factory.h" TEST(ScsiCdTest, DeviceDefaults) { - auto device = DeviceFactory::Instance().CreateDevice(UNDEFINED, 0, "test.iso"); - EXPECT_NE(nullptr, device); - EXPECT_EQ(SCCD, device->GetType()); - EXPECT_TRUE(device->SupportsFile()); - EXPECT_FALSE(device->SupportsParams()); - EXPECT_FALSE(device->IsProtectable()); - EXPECT_FALSE(device->IsProtected()); - EXPECT_TRUE(device->IsReadOnly()); - EXPECT_TRUE(device->IsRemovable()); - EXPECT_FALSE(device->IsRemoved()); - EXPECT_TRUE(device->IsLockable()); - EXPECT_FALSE(device->IsLocked()); - EXPECT_TRUE(device->IsStoppable()); - EXPECT_FALSE(device->IsStopped()); - - EXPECT_EQ("SCSI2Pi", device->GetVendor()); - EXPECT_EQ("SCSI CD-ROM", device->GetProduct()); - EXPECT_EQ(TestShared::GetVersion(), device->GetRevision()); + MockScsiCd cd(0); + + EXPECT_EQ(SCCD, cd.GetType()); + EXPECT_TRUE(cd.SupportsFile()); + EXPECT_FALSE(cd.SupportsParams()); + EXPECT_FALSE(cd.IsProtectable()); + EXPECT_FALSE(cd.IsProtected()); + EXPECT_TRUE(cd.IsReadOnly()); + EXPECT_TRUE(cd.IsRemovable()); + EXPECT_FALSE(cd.IsRemoved()); + EXPECT_TRUE(cd.IsLockable()); + EXPECT_FALSE(cd.IsLocked()); + EXPECT_TRUE(cd.IsStoppable()); + EXPECT_FALSE(cd.IsStopped()); + + EXPECT_EQ("SCSI2Pi", cd.GetVendor()); + EXPECT_EQ("SCSI CD-ROM", cd.GetProduct()); + EXPECT_EQ(TestShared::GetVersion(), cd.GetRevision()); } void ScsiCdTest_SetUpModePages(map> &pages) @@ -86,11 +84,11 @@ TEST(ScsiCdTest, Open) EXPECT_THROW(cd.Open(), io_exception)<< "Missing filename"; path filename = CreateTempFile(2047); - cd.SetFilename(string(filename)); + cd.SetFilename(filename.string()); EXPECT_THROW(cd.Open(), io_exception)<< "ISO CD-ROM image file size is too small"; filename = CreateTempFile(2 * 2048); - cd.SetFilename(string(filename)); + cd.SetFilename(filename.string()); cd.Open(); EXPECT_EQ(2U, cd.GetBlockCount()); diff --git a/cpp/test/scsi_controller_test.cpp b/cpp/test/scsi_controller_test.cpp deleted file mode 100644 index d3aec627..00000000 --- a/cpp/test/scsi_controller_test.cpp +++ /dev/null @@ -1,316 +0,0 @@ -//--------------------------------------------------------------------------- -// -// SCSI target emulator and SCSI tools for the Raspberry Pi -// -// Copyright (C) 2022-2024 Uwe Seimet -// -//--------------------------------------------------------------------------- - -#include "mocks.h" -#include "shared/shared_exceptions.h" - -using namespace scsi_defs; - -TEST(ScsiControllerTest, Reset) -{ - const int ID = 5; - - NiceMock bus; - auto controller = make_shared(bus, ID, 32); - auto device = make_shared(0); - - controller->AddDevice(device); - - controller->Process(ID); - EXPECT_EQ(ID, controller->GetInitiatorId()); - controller->Reset(); - EXPECT_EQ(-1, controller->GetInitiatorId()); -} - -TEST(ScsiControllerTest, GetInitiatorId) -{ - const int ID = 2; - - auto bus = make_shared>(); - MockScsiController controller(bus, 0); - auto device = make_shared(0); - - controller.AddDevice(device); - - EXPECT_CALL(controller, Status).Times(2); - controller.Process(ID); - EXPECT_EQ(ID, controller.GetInitiatorId()); - controller.Process(1234); - EXPECT_EQ(1234, controller.GetInitiatorId()); -} - -TEST(ScsiControllerTest, Process) -{ - auto bus = make_shared>(); - MockScsiController controller(bus, 0); - auto device = make_shared(0); - - controller.Init(); - controller.AddDevice(device); - - controller.SetPhase(phase_t::reserved); - ON_CALL(*bus, GetRST).WillByDefault(Return(true)); - EXPECT_CALL(*bus, Acquire); - EXPECT_CALL(*bus, GetRST); - EXPECT_CALL(controller, Reset()); - EXPECT_FALSE(controller.Process(0)); - - controller.SetPhase(phase_t::busfree); - ON_CALL(*bus, GetRST).WillByDefault(Return(false)); - EXPECT_CALL(*bus, Acquire); - EXPECT_CALL(*bus, GetRST); - EXPECT_CALL(controller, Status()); - EXPECT_FALSE(controller.Process(0)); - - controller.SetPhase(phase_t::reserved); - EXPECT_CALL(*bus, Acquire).Times(2); - EXPECT_CALL(*bus, GetRST).Times(2); - EXPECT_FALSE(controller.Process(0)); -} - -TEST(ScsiControllerTest, BusFree) -{ - auto bus = make_shared>(); - MockScsiController controller(bus, 0); - - controller.SetPhase(phase_t::busfree); - controller.BusFree(); - EXPECT_EQ(phase_t::busfree, controller.GetPhase()); - - controller.SetStatus(status::check_condition); - controller.SetPhase(phase_t::reserved); - controller.BusFree(); - EXPECT_EQ(phase_t::busfree, controller.GetPhase()); - EXPECT_EQ(status::good, controller.GetStatus()); - - controller.ScheduleShutdown(AbstractController::shutdown_mode::none); - controller.SetPhase(phase_t::reserved); - controller.BusFree(); - - controller.ScheduleShutdown(AbstractController::shutdown_mode::stop_pi); - controller.SetPhase(phase_t::reserved); - controller.BusFree(); - - controller.ScheduleShutdown(AbstractController::shutdown_mode::restart_pi); - controller.SetPhase(phase_t::reserved); - controller.BusFree(); - - controller.ScheduleShutdown(AbstractController::shutdown_mode::stop_s2p); - controller.SetPhase(phase_t::reserved); - controller.BusFree(); -} - -TEST(ScsiControllerTest, Selection) -{ - auto bus = make_shared>(); - auto controller = make_shared(bus, 0); - auto device = make_shared(0); - - controller->AddDevice(device); - - controller->SetPhase(phase_t::selection); - ON_CALL(*bus, GetSEL).WillByDefault(Return(true)); - ON_CALL(*bus, GetBSY).WillByDefault(Return(true)); - EXPECT_CALL(*bus, GetATN).Times(0); - controller->Selection(); - EXPECT_EQ(phase_t::selection, controller->GetPhase()); - - ON_CALL(*bus, GetSEL).WillByDefault(Return(true)); - ON_CALL(*bus, GetBSY).WillByDefault(Return(false)); - EXPECT_CALL(*bus, GetATN).Times(0); - EXPECT_CALL(*controller, Status); - controller->Selection(); - EXPECT_EQ(phase_t::selection, controller->GetPhase()); - - ON_CALL(*bus, GetSEL).WillByDefault(Return(false)); - ON_CALL(*bus, GetBSY).WillByDefault(Return(false)); - EXPECT_CALL(*bus, GetATN).Times(0); - controller->Selection(); - EXPECT_EQ(phase_t::selection, controller->GetPhase()); - - ON_CALL(*bus, GetSEL).WillByDefault(Return(false)); - ON_CALL(*bus, GetBSY).WillByDefault(Return(true)); - ON_CALL(*bus, GetATN).WillByDefault(Return(false)); - EXPECT_CALL(*bus, GetATN); - controller->Selection(); - EXPECT_EQ(phase_t::command, controller->GetPhase()); - - controller->SetPhase(phase_t::selection); - ON_CALL(*bus, GetSEL).WillByDefault(Return(false)); - ON_CALL(*bus, GetBSY).WillByDefault(Return(true)); - ON_CALL(*bus, GetATN).WillByDefault(Return(true)); - EXPECT_CALL(*bus, GetATN); - controller->Selection(); - EXPECT_EQ(phase_t::msgout, controller->GetPhase()); - - ON_CALL(*bus, GetDAT).WillByDefault(Return(1)); - EXPECT_CALL(*bus, SetBSY(true)); - controller->Selection(); - EXPECT_EQ(phase_t::selection, controller->GetPhase()); -} - -TEST(ScsiControllerTest, Command) -{ - auto bus = make_shared>(); - MockScsiController controller(bus, 0); - auto device = make_shared(0); - - controller.AddDevice(device); - - controller.SetPhase(phase_t::command); - EXPECT_CALL(controller, Status).Times(2); - controller.Command(); - EXPECT_EQ(phase_t::command, controller.GetPhase()); - - controller.SetPhase(phase_t::reserved); - EXPECT_CALL(*bus, SetMSG(false)); - EXPECT_CALL(*bus, SetCD(true)); - EXPECT_CALL(*bus, SetIO(false)); - controller.Command(); - EXPECT_EQ(phase_t::command, controller.GetPhase()); - - controller.SetPhase(phase_t::reserved); - ON_CALL(*bus, CommandHandShake).WillByDefault(Return(6)); - EXPECT_CALL(*bus, SetMSG(false)); - EXPECT_CALL(*bus, SetCD(true)); - EXPECT_CALL(*bus, SetIO(false)); - controller.Command(); - EXPECT_EQ(phase_t::command, controller.GetPhase()); -} - -TEST(ScsiControllerTest, MsgIn) -{ - auto bus = make_shared>(); - MockScsiController controller(bus, 0); - - controller.SetPhase(phase_t::reserved); - EXPECT_CALL(*bus, SetMSG(true)); - EXPECT_CALL(*bus, SetCD(true)); - EXPECT_CALL(*bus, SetIO(true)); - controller.MsgIn(); - EXPECT_EQ(phase_t::msgin, controller.GetPhase()); - EXPECT_EQ(0, controller.GetOffset()); - EXPECT_EQ(0, controller.GetCurrentLength()); -} - -TEST(ScsiControllerTest, MsgOut) -{ - auto bus = make_shared>(); - MockScsiController controller(bus, 0); - - controller.SetPhase(phase_t::reserved); - EXPECT_CALL(*bus, SetMSG(true)); - EXPECT_CALL(*bus, SetCD(true)); - EXPECT_CALL(*bus, SetIO(false)); - controller.MsgOut(); - EXPECT_EQ(phase_t::msgout, controller.GetPhase()); - EXPECT_EQ(0, controller.GetOffset()); - EXPECT_EQ(1, controller.GetCurrentLength()); -} - -TEST(ScsiControllerTest, DataIn) -{ - auto bus = make_shared>(); - MockScsiController controller(bus, 0); - - controller.SetPhase(phase_t::reserved); - controller.SetCurrentLength(0); - EXPECT_CALL(controller, Status); - controller.DataIn(); - EXPECT_EQ(phase_t::reserved, controller.GetPhase()); - - controller.SetCurrentLength(1); - EXPECT_CALL(*bus, SetMSG(false)); - EXPECT_CALL(*bus, SetCD(false)); - EXPECT_CALL(*bus, SetIO(true)); - controller.DataIn(); - EXPECT_EQ(phase_t::datain, controller.GetPhase()); - EXPECT_EQ(0, controller.GetOffset()); -} - -TEST(ScsiControllerTest, DataOut) -{ - auto bus = make_shared>(); - MockScsiController controller(bus, 0); - - controller.SetPhase(phase_t::reserved); - controller.SetCurrentLength(0); - EXPECT_CALL(controller, Status); - controller.DataOut(); - EXPECT_EQ(phase_t::reserved, controller.GetPhase()); - - controller.SetCurrentLength(1); - EXPECT_CALL(*bus, SetMSG(false)); - EXPECT_CALL(*bus, SetCD(false)); - EXPECT_CALL(*bus, SetIO(false)); - controller.DataOut(); - EXPECT_EQ(phase_t::dataout, controller.GetPhase()); - EXPECT_EQ(0, controller.GetOffset()); -} - -TEST(ScsiControllerTest, Error) -{ - auto bus = make_shared>(); - MockScsiController controller(bus, 0); - auto device = make_shared(0); - - controller.AddDevice(device); - - ON_CALL(*bus, GetRST).WillByDefault(Return(true)); - controller.SetPhase(phase_t::reserved); - EXPECT_CALL(*bus, Acquire); - EXPECT_CALL(*bus, GetRST()); - EXPECT_CALL(controller, Reset).Times(0); - controller.Error(sense_key::aborted_command, asc::no_additional_sense_information, status::reservation_conflict); - EXPECT_EQ(status::good, controller.GetStatus()); - EXPECT_EQ(phase_t::busfree, controller.GetPhase()); - - ON_CALL(*bus, GetRST).WillByDefault(Return(false)); - controller.SetPhase(phase_t::status); - EXPECT_CALL(*bus, Acquire); - EXPECT_CALL(*bus, GetRST()); - EXPECT_CALL(controller, Reset).Times(0); - controller.Error(sense_key::aborted_command, asc::no_additional_sense_information, status::reservation_conflict); - EXPECT_EQ(phase_t::busfree, controller.GetPhase()); - - controller.SetPhase(phase_t::msgin); - EXPECT_CALL(*bus, Acquire); - EXPECT_CALL(*bus, GetRST()); - EXPECT_CALL(controller, Reset).Times(0); - controller.Error(sense_key::aborted_command, asc::no_additional_sense_information, status::reservation_conflict); - EXPECT_EQ(phase_t::busfree, controller.GetPhase()); - - controller.SetPhase(phase_t::reserved); - EXPECT_CALL(*bus, Acquire); - EXPECT_CALL(*bus, GetRST()); - EXPECT_CALL(controller, Reset).Times(0); - EXPECT_CALL(controller, Status); - controller.Error(sense_key::aborted_command, asc::no_additional_sense_information, status::reservation_conflict); - EXPECT_EQ(status::reservation_conflict, controller.GetStatus()); - EXPECT_EQ(phase_t::reserved, controller.GetPhase()); -} - -TEST(ScsiControllerTest, RequestSense) -{ - auto bus = make_shared>(); - auto controller = make_shared(bus, 0); - auto device = make_shared(0); - EXPECT_TRUE(device->Init( { })); - - controller->AddDevice(device); - - // ALLOCATION LENGTH - controller->SetCdbByte(4, 255); - // Non-existing LUN - controller->SetCdbByte(1, 0x20); - - device->SetReady(true); - EXPECT_CALL(*controller, Status); - EXPECT_NO_THROW(device->Dispatch(scsi_command::cmd_request_sense)); - EXPECT_EQ(status::good, controller->GetStatus()) << "Wrong CHECK CONDITION for non-existing LUN"; -} diff --git a/cpp/test/scsi_hd_test.cpp b/cpp/test/scsi_hd_test.cpp index 926eafe3..1af17ee1 100644 --- a/cpp/test/scsi_hd_test.cpp +++ b/cpp/test/scsi_hd_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -90,13 +90,6 @@ TEST(ScsiHdTest, Inquiry) false, "file.hd1"); } -TEST(ScsiHdTest, SupportsSaveParameters) -{ - MockScsiHd hd(0, false); - - EXPECT_TRUE(hd.SupportsSaveParameters()); -} - TEST(ScsiHdTest, FinalizeSetup) { MockScsiHd hd(0, false); @@ -112,21 +105,21 @@ TEST(ScsiHdTest, GetProductData) MockScsiHd hd_gb(0, false); const path filename = CreateTempFile(1); - hd_kb.SetFilename(string(filename)); + hd_kb.SetFilename(filename.string()); hd_kb.SetSectorSizeInBytes(1024); hd_kb.SetBlockCount(1); hd_kb.FinalizeSetup(); string s = hd_kb.GetProduct(); EXPECT_NE(string::npos, s.find("1 KiB")); - hd_mb.SetFilename(string(filename)); + hd_mb.SetFilename(filename.string()); hd_mb.SetSectorSizeInBytes(1024); hd_mb.SetBlockCount(1'048'576 / 1024); hd_mb.FinalizeSetup(); s = hd_mb.GetProduct(); EXPECT_NE(string::npos, s.find("1 MiB")); - hd_gb.SetFilename(string(filename)); + hd_gb.SetFilename(filename.string()); hd_gb.SetSectorSizeInBytes(1024); hd_gb.SetBlockCount(10'737'418'240 / 1024); hd_gb.FinalizeSetup(); @@ -251,14 +244,13 @@ TEST(ScsiHdTest, ModeSelect) TEST(ScsiHdTest, ModeSelect6_Single) { - const int LENGTH = 28; - vector buf(LENGTH); + vector buf(28); MockScsiHd hd( { 512, 1024, 2048 }); // PF (vendor-specific parameter format) must not fail but be ignored vector cdb = CreateCdb(scsi_command::cmd_mode_select6, "00:00:00:00:00"); hd.SetSectorSizeInBytes(1024); - EXPECT_NO_THROW(hd.ModeSelect(scsi_command::cmd_mode_select6, cdb, buf, LENGTH)); + EXPECT_NO_THROW(hd.ModeSelect(scsi_command::cmd_mode_select6, cdb, buf, buf.size())); EXPECT_EQ(1024U, hd.GetSectorSizeInBytes()); // PF (standard parameter format) @@ -271,7 +263,7 @@ TEST(ScsiHdTest, ModeSelect6_Single) // Page 0 buf[4] = 0x00; - EXPECT_THAT([&] {hd.ModeSelect(scsi_command::cmd_mode_select6, cdb, buf, LENGTH);}, + EXPECT_THAT([&] {hd.ModeSelect(scsi_command::cmd_mode_select6, cdb, buf, buf.size());}, Throws(AllOf( Property(&scsi_exception::get_sense_key, sense_key::illegal_request), Property(&scsi_exception::get_asc, asc::invalid_field_in_parameter_list)))) @@ -305,7 +297,7 @@ TEST(ScsiHdTest, ModeSelect6_Single) buf[4] = 0x03; // Page length buf[5] = 0x16; - EXPECT_THAT([&] {hd.ModeSelect(scsi_command::cmd_mode_select6, cdb, buf, LENGTH);}, + EXPECT_THAT([&] {hd.ModeSelect(scsi_command::cmd_mode_select6, cdb, buf, buf.size());}, Throws(AllOf( Property(&scsi_exception::get_sense_key, sense_key::illegal_request), Property(&scsi_exception::get_asc, asc::invalid_field_in_parameter_list)))) @@ -314,13 +306,13 @@ TEST(ScsiHdTest, ModeSelect6_Single) // Match the requested to the current sector size buf[16] = 0x08; hd.SetSectorSizeInBytes(2048); - EXPECT_THAT([&] {hd.ModeSelect(scsi_command::cmd_mode_select6, cdb, buf, LENGTH - 10);}, + EXPECT_THAT([&] {hd.ModeSelect(scsi_command::cmd_mode_select6, cdb, buf, buf.size() - 10);}, Throws(AllOf( Property(&scsi_exception::get_sense_key, sense_key::illegal_request), Property(&scsi_exception::get_asc, asc::parameter_list_length_error)))) << "Not enough command parameters"; - EXPECT_NO_THROW(hd.ModeSelect(scsi_command::cmd_mode_select6, cdb, buf, LENGTH)); + EXPECT_NO_THROW(hd.ModeSelect(scsi_command::cmd_mode_select6, cdb, buf, buf.size())); EXPECT_EQ(2048U, hd.GetSectorSizeInBytes()); } @@ -368,14 +360,13 @@ TEST(ScsiHdTest, ModeSelect6_Multiple) TEST(ScsiHdTest, ModeSelect10_Single) { - const int LENGTH = 32; - vector buf(LENGTH); + vector buf(32); MockScsiHd hd( { 512, 1024, 2048 }); // PF (vendor-specific parameter format) must not fail but be ignored vector cdb = CreateCdb(scsi_command::cmd_mode_select10, "00:00:00:00:00:00:00:00:00"); hd.SetSectorSizeInBytes(1024); - EXPECT_NO_THROW(hd.ModeSelect(scsi_command::cmd_mode_select10, cdb, buf, LENGTH)); + EXPECT_NO_THROW(hd.ModeSelect(scsi_command::cmd_mode_select10, cdb, buf, buf.size())); EXPECT_EQ(1024U, hd.GetSectorSizeInBytes()); // PF (standard parameter format) @@ -388,7 +379,7 @@ TEST(ScsiHdTest, ModeSelect10_Single) // Page 0 buf[8] = 0x00; - EXPECT_THAT([&] {hd.ModeSelect(scsi_command::cmd_mode_select10, cdb, buf, LENGTH);}, + EXPECT_THAT([&] {hd.ModeSelect(scsi_command::cmd_mode_select10, cdb, buf, buf.size());}, Throws(AllOf( Property(&scsi_exception::get_sense_key, sense_key::illegal_request), Property(&scsi_exception::get_asc, asc::invalid_field_in_parameter_list)))) @@ -422,7 +413,7 @@ TEST(ScsiHdTest, ModeSelect10_Single) buf[8] = 0x03; // Page length buf[9] = 0x16; - EXPECT_THAT([&] {hd.ModeSelect(scsi_command::cmd_mode_select10, cdb, buf, LENGTH);}, + EXPECT_THAT([&] {hd.ModeSelect(scsi_command::cmd_mode_select10, cdb, buf, buf.size());}, Throws(AllOf( Property(&scsi_exception::get_sense_key, sense_key::illegal_request), Property(&scsi_exception::get_asc, asc::invalid_field_in_parameter_list)))) @@ -431,13 +422,13 @@ TEST(ScsiHdTest, ModeSelect10_Single) // Match the requested to the current sector size buf[20] = 0x08; hd.SetSectorSizeInBytes(2048); - EXPECT_THAT([&] {hd.ModeSelect(scsi_command::cmd_mode_select10, cdb, buf, LENGTH - 10);}, + EXPECT_THAT([&] {hd.ModeSelect(scsi_command::cmd_mode_select10, cdb, buf, buf.size() - 10);}, Throws(AllOf( Property(&scsi_exception::get_sense_key, sense_key::illegal_request), Property(&scsi_exception::get_asc, asc::parameter_list_length_error)))) << "Not enough command parameters"; - EXPECT_NO_THROW(hd.ModeSelect(scsi_command::cmd_mode_select10, cdb, buf, LENGTH)); + EXPECT_NO_THROW(hd.ModeSelect(scsi_command::cmd_mode_select10, cdb, buf, buf.size())); EXPECT_EQ(2048U, hd.GetSectorSizeInBytes()); } diff --git a/cpp/test/shared_exceptions_test.cpp b/cpp/test/shared_exceptions_test.cpp index 499c6b8b..91181930 100644 --- a/cpp/test/shared_exceptions_test.cpp +++ b/cpp/test/shared_exceptions_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // diff --git a/cpp/test/storage_device_test.cpp b/cpp/test/storage_device_test.cpp index 5af5b413..b1e440db 100644 --- a/cpp/test/storage_device_test.cpp +++ b/cpp/test/storage_device_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -30,7 +30,7 @@ TEST(StorageDeviceTest, ValidateFile) EXPECT_THROW(device.ValidateFile(), io_exception); const path filename = CreateTempFile(1); - device.SetFilename(string(filename)); + device.SetFilename(filename.string()); device.SetReadOnly(false); device.SetProtectable(true); device.ValidateFile(); @@ -65,12 +65,32 @@ TEST(StorageDeviceTest, MediumChanged) EXPECT_FALSE(device.IsMediumChanged()); } +TEST(StorageDeviceTest, ReserveUnreserveFile) +{ + MockStorageDevice device1; + MockStorageDevice device2; + + device1.SetFilename(""); + EXPECT_FALSE(device1.ReserveFile()); + device1.SetFilename("filename1"); + EXPECT_TRUE(device1.ReserveFile()); + EXPECT_FALSE(device1.ReserveFile()); + device2.SetFilename("filename1"); + EXPECT_FALSE(device2.ReserveFile()); + device2.SetFilename("filename2"); + EXPECT_TRUE(device2.ReserveFile()); + device1.UnreserveFile(); + EXPECT_TRUE(device1.GetFilename().empty()); + device2.UnreserveFile(); + EXPECT_TRUE(device2.GetFilename().empty()); +} + TEST(StorageDeviceTest, GetIdsForReservedFile) { const int ID = 1; const int LUN = 0; auto bus = make_shared(); - ControllerFactory controller_factory; + ControllerFactory controller_factory(false); MockAbstractController controller(bus, ID); auto device = make_shared(LUN, false); device->SetFilename("filename"); @@ -98,7 +118,7 @@ TEST(StorageDeviceTest, GetSetReservedFiles) const int ID = 1; const int LUN = 0; auto bus = make_shared(); - ControllerFactory controller_factory; + ControllerFactory controller_factory(false); MockAbstractController controller(bus, ID); auto device = make_shared(LUN, false); device->SetFilename("filename"); diff --git a/cpp/test/tap_driver_test.cpp b/cpp/test/tap_driver_test.cpp index b05938c3..90c80d37 100644 --- a/cpp/test/tap_driver_test.cpp +++ b/cpp/test/tap_driver_test.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // diff --git a/cpp/test/test_setup.cpp b/cpp/test/test_setup.cpp index 2ae52224..d1a08c2b 100644 --- a/cpp/test/test_setup.cpp +++ b/cpp/test/test_setup.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // diff --git a/cpp/test/test_shared.cpp b/cpp/test/test_shared.cpp index b8716b14..daa37c71 100644 --- a/cpp/test/test_shared.cpp +++ b/cpp/test/test_shared.cpp @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // @@ -11,6 +11,7 @@ #include #include "mocks.h" #include "shared/s2p_version.h" +#include "shared/shared_exceptions.h" #include "base/device_factory.h" #include "buses/bus_factory.h" @@ -117,7 +118,7 @@ void testing::TestShared::Dispatch(PrimaryDevice &device, scsi_command cmd, sens pair testing::OpenTempFile() { const string filename = fmt::format("/tmp/scsi2pi_test-{}-XXXXXX", getpid()); // NOSONAR Publicly writable directory is fine here - vector f(filename.begin(), filename.end()); + vector f(filename.cbegin(), filename.cend()); f.emplace_back(0); const int fd = mkstemp(f.data()); diff --git a/cpp/test/test_shared.h b/cpp/test/test_shared.h index 83255d2d..3ebe1e1f 100644 --- a/cpp/test/test_shared.h +++ b/cpp/test/test_shared.h @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2022-2024 Uwe Seimet // diff --git a/doc/s2p.1 b/doc/s2p.1 index 171c0f5a..78153d11 100644 --- a/doc/s2p.1 +++ b/doc/s2p.1 @@ -28,7 +28,7 @@ s2p \- Emulates SCSI and SASI devices with the Raspberry Pi emulates SCSI and SASI devices with the Raspberry Pi and a PiSCSI/RaSCSI board. .PP In the arguments to s2p one or more SCSI (-i n[:u]) or SASI (-h n[:u]) devices can be specified. SCSI and SASI devices cannot be mixed. -The number (n) after the ID or HD identifier specifies the ID number for that device. The optional number (u) specifies the LUN (logical unit) for that device. The default LUN is 0. +The number (n) after the device identifier specifies the ID number for that device. The optional number (u) specifies the LUN (logical unit) for that device. The default LUN is 0. The ID is limited from 0-7. However, typically SCSI ID 7 is reserved for the "initiator" (the host computer). The LUN is limited from 0-31 for SCSI and 0-1 for SASI. .PP If no explicit device type is provided, s2p determines the type based upon the file extension of the FILE argument. @@ -49,80 +49,76 @@ To quit s2p press Control-C. If it is running in the background, you can kill it .SH OPTIONS .TP -.BR \--blue-scsi-mode/-B\fI " " \fI +.BR --blue-scsi-mode/-B\fI " " \fI Enable BlueSCSI filename parsing compatibility mode for the respective device. In this mode the name of an image file is parsed based on the BlueSCSI parsing rules. This mode is useful in order to share drive images between SCSI2Pi and BlueSCSI. If there are SCSI2Pi command line options for the respective device they will override the settings derived from the filename. If there is an additional text before the filename extension s2p tries to parse this text as INQUIRY product data. .TP -.BR \--scsi-id/-i \fI " "\fIn[:u] " " \fIFILE +.BR --scsi-id/-i \fI " "\fIn[:u] " " \fIFILE n is the SCSI ID (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 the filename may have a special meaning or a dummy name can be provided. For SCDP it is an optional prioritized list of network interfaces, an optional IP address and netmask, e.g. "interface=eth0,eth1,wlan0:inet=10.10.20.1/24". For SCLP it is the print command to be used and a reservation timeout in seconds, e.g. "cmd=lp -oraw %f:timeout=60". .TP -.BR \--sasi-id/-h\fI " "\fIn[:u] " " \fIFILE +.BR --sasi-id/-h\fI " "\fIn[:u] " " \fIFILE n is the SASI ID (0-7). u (0-1) is the optional LUN (logical unit). The default LUN is 0. .IP FILE is the name of the image file to use for the SASI hard drive. .TP -.BR \--type/-t\fI " " \fITYPE +.BR --type/-t\fI " " \fITYPE The optional case-insensitive device type (SAHD, SCHD, SCRM, SCCD, SCMO, SCDP, SCLP, SCHS). If no type is specified for devices that support an image file, s2p tries to derive the type from the file extension. .TP -.BR \--scsi-level\fI " " \fITYPE +.BR --scsi-level\fI " " \fITYPE The optional SCSI standard level. The default level is device-specific and usually is SCSI-2. Be careful with using this option, you will usually not need it. Explicitly setting the level may be required for drives with removable media if they are attached without providing an image file. In this case the SCSI-1-CCS level cannot be derived from the ".is1" or ".hd1" filename extension and "--scsi-level 1" can be used. .TP -.BR \--name/-n\fI " " \fIVENDOR:PRODUCT:REVISION +.BR --name/-n\fI " " \fIVENDOR:PRODUCT:REVISION Set the optional vendor, product and revision for the device, to be returned with the INQUIRY data. A complete set of name components must be provided. VENDOR may have up to 8, PRODUCT up to 16, REVISION up to 4 characters. Padding with blanks to the maxium length is automatically applied. Once set the name of a device cannot be changed. .TP -.BR \--block-size/-b\fI " " \fIBLOCK_SIZE +.BR --block-size/-b\fI " " \fIBLOCK_SIZE The optional sector size, usually 256 (SASI only), 512, 1024, 2048 or 4096 bytes. The default size for SASI hard drives is 256 bytes, for SCSI hard drives it is 512 bytes. s2p supports non-standard SCSI hard drive sector sizes as long as they are multiples of 4, but these sizes are only required for exotic platforms. .TP -.BR \--caching-mode/-m\fI " " \fICACHING_MODE +.BR --caching-mode/-m\fI " " \fICACHING_MODE Caching mode (piscsi|write-through|linux|linux-optimized), default currently is PiSCSI compatible caching. .TP -.BR \--blue-scsi-mode/-B\fI " " \fI -Enable BlueSCSI filename compatibility mode. See the BlueSCSI documentation for details on the BlueSCSI filename convention. -.TP -.BR \--reserved-ids/-r\fI " " \fIRESERVED_IDS +.BR --reserved-ids/-r\fI " " \fIRESERVED_IDS An optional comma-separated list of IDs to reserve. Pass an empty list in order to not reserve any ID. .TP -.BR \--image-folder/-F\fI " " \fIFOLDER +.BR --image-folder/-F\fI " " \fIFOLDER The default folder for image files. For files in this folder no absolute path needs to be specified. The default folder is '~/images' except for the root user. In this case, for backwards compatibility with PiSCSI the default is '/home/pi/images'. .TP -.BR \--scan-depth/-R\fI " " \fISCAN_DEPTH +.BR --scan-depth/-R\fI " " \fISCAN_DEPTH Scan for image files recursively, up to a depth of SCAN_DEPTH. Depth 0 means to ignore any folders within the default image folder. Be careful when using this option with many sub-folders in the default image folder. The default depth is 1. .TP -.BR \--property-\fI " " \fIKEY=VALUE +.BR --property-\fI " " \fIKEY=VALUE Set the s2p configuration property with the name KEY to the value VALUE. Properties set with this option override properties from property files. .TP -.BR \--property-files/-C\fI " " \fIPROPERTY_FILES -An optional comma-separated list of property files with s2p configuration data. The optional default global property file is /etc/s2p.conf. This file is parsed first. +.BR --property-files/-C\fI " " \fIPROPERTY_FILES +An optional comma-separated list of property files with s2p configuration data. The default global property file is /etc/s2p.conf. This file is parsed first. The optional local default property file is ~/.config/s2p.conf. Definitions in this file and in explicitly specified property files override the previously defined properties. .TP -.BR \--log-level/-L\fI " " \fILOG_LEVEL[:ID:[LUN]] +.BR --log-level/-L\fI " " \fILOG_LEVEL[:ID:[LUN]] The s2p log level (trace, debug, info, warning, error, critical, off). The default log level is 'info' for all devices unless a device ID and an optional LUN is provided when setting the level. .TP -.BR \--log-pattern/-l\fI " " \fILOG_PATTERN +.BR --log-pattern/-l\fI " " \fILOG_PATTERN The spdlog pattern to use for logging. See https://github.com/gabime/spdlog for details. .TP -.BR \--token-file/-P\fI " " \fIACCESS_TOKEN_FILE +.BR --token-file/-P\fI " " \fIACCESS_TOKEN_FILE Enable authentication and read the access token from the specified file. The access token file must be owned by root and must be readable by root only. .TP -.BR \--port/-p\fI " " \fIPORT +.BR --port/-p\fI " " \fIPORT The s2p client service port, default is 6868. .TP -.BR \--locale,-z\fI " "\fILOCALE +.BR --locale/-z\fI " " \fILOCALE Overrides the default locale (language) for client-facing error messages. A client can override this setting. .TP -.BR \--version/-v\fI " " \fI +.BR --version/-v\fI " " \fI Displays the s2p version. .TP -.BR \--help\fI " " \fI -Display a help page. -.IP +.BR --help\fI " " \fI +Displays a help page. .SH EXAMPLES Launch s2p with no devices attached: diff --git a/doc/s2pctl.1 b/doc/s2pctl.1 index 1983df5e..727ecc71 100644 --- a/doc/s2pctl.1 +++ b/doc/s2pctl.1 @@ -52,104 +52,104 @@ In interactive mode the host and port settings are preserved between command inv .SH OPTIONS .TP -.BR \-id/-i\fI " " \fIID[:LUN] +.BR --id/-i\fI " " \fIID[:LUN] The SCSI or SASI ID and optional LUN that you want to control. (0-7:0-31 for SCSI, 0-7:0-1 for SASI.) .TP -.BR \--create/-C\fI " "\fIFILENAME:FILESIZE +.BR --create/-C\fI " "\fIFILENAME:FILESIZE Create an image file in the default image folder with the specified name and size in bytes. .TP -.BR \--detach-all/-D\fI +.BR --detach-all/-D\fI Detach all devices. .TP -.BR \--list-image-info/-E\fI " " \fIFILENAME +.BR --list-image-info/-E\fI " " \fIFILENAME Display image file information. .TP -.BR \--image-folder/-F\fI " "\fIIMAGE_FOLDER +.BR --image-folder/-F\fI " "\fIIMAGE_FOLDER Set the default image folder. .TP -.BR \-list-reserved-ids/-I\fI +.BR --list-reserved-ids/-I\fI Display reserved device IDs. .TP -.BR \--log-level/-L\fI " "\fILOG_LEVEL[:ID[:LUN]] +.BR --log-level/-L\fI " "\fILOG_LEVEL[:ID[:LUN]] Set the s2p log level (trace, debug, info, warning, error, critical, off). .TP -.BR \--host/-H\fI " " \fIHOST +.BR --host/-H\fI " " \fIHOST The s2p host to connect to, default is 'localhost'. .TP -.BR \--list-interfaces/-N\fI +.BR --list-interfaces/-N\fI Lists all network interfaces that are up and available for setting up a DaynaPort emulation. .TP -.BR \--list-log-levels\fI +.BR --list-log-levels\fI Display the available s2p log levels and the current log level. .TP -.BR \--prompt\fI +.BR --prompt\fI Prompt for the access token in case s2p requires authentication. .TP -.BR \--list-devices/-l\fI +.BR --list-devices/-l\fI List all of the devices that are currently being emulated, as well as their current status. .TP -.BR \--list-extensions\fI +.BR --list-extensions\fI List all supported file extensions and the device types they map to. .TP -.BR \--list-images/-e\fI +.BR --list-images/-e\fI List all images files in the default image folder. .TP -.BR \--list-operations/-o\fI +.BR --list-operations/-o\fI List available remote interface operations. .TP -.BR \--rename/-R\fI " "\fICURRENT_NAME:NEW_NAME +.BR --rename/-R\fI " "\fICURRENT_NAME:NEW_NAME Rename an image file in the default image folder. .TP -.BR \--port/-p\fI " " \fIPORT +.BR --port/-p\fI " " \fIPORT The s2p port to connect to, default is 6868. .TP -.BR \--list-properties/-P\fI " " \fIPORT +.BR --list-properties/-P\fI " " \fIPORT Display the current s2p properties. The resulting output can directly be used for property files, e.g. for /etc/s2p.conf when running s2p as a system service. .TP -.BR \--reserved-ids/-r\fI " " \fIRESERVED_IDS +.BR --reserved-ids/-r\fI " " \fIRESERVED_IDS Comma-separated list of IDs to reserve. Pass an empty list in order to not reserve anything. .TP -.BR \--list-settings/-s\fI " " \fI[FOLDER_PATTERN:FILE_PATTERN:OPERATIONS] +.BR --list-settings/-s\fI " " \fI[FOLDER_PATTERN:FILE_PATTERN:OPERATIONS] Display all s2p settings. Filenames are optionally filtered by folder and file patterns. OPERATIONS is an optional comma-seperated list of server operations to take into account, in order to limit the amount of output. .TP -.BR \--list-statistics/-S\fI +.BR --list-statistics/-S\fI Display s2p statistics. .TP -.BR \--list-device-types/-T\fI +.BR --list-device-types/-T\fI Display available device types and their properties. .TP -.BR \--help/-h\fI " " \fI +.BR --help/-h\fI " " \fI Display usage information. .TP -.BR \-version/-v\fI " " \fI +.BR --version/-v\fI " " \fI Display the s2pctl version. .TP -.BR \--server-version/-V\fI " " \fI +.BR --server-version/-V\fI " " \fI Display the s2p server version. .TP -.BR \--shut-down/-X\fI " " \fI +.BR --shut-down/-X\fI " " \fI Shut down the s2p process. .TP -.BR \--delete/-d\fI " "\fIFILENAME +.BR --delete/-d\fI " "\fIFILENAME Delete an image file in the default image folder. .TP -.BR \--copy/-x\fI " "\fICURRENT_NAME:NEW_NAME +.BR --copy/-x\fI " "\fICURRENT_NAME:NEW_NAME Copy an image file in the default image folder. .TP -.BR \--binary-protobuf\fI " "\fIFILENAME +.BR --binary-protobuf\fI " "\fIFILENAME Do not send the command to s2p but write it to a protobuf binary file. .TP -.BR \--json-protobuf\fI " "\fIFILENAME +.BR --json-protobuf\fI " "\fIFILENAME Do not send the command to s2p but write it to a protobuf JSON file. .TP -.BR \--text-protobuf\fI " "\fIFILENAME +.BR --text-protobuf\fI " "\fIFILENAME Do not send the command to s2p but write it to a protobuf text format file. .TP -.BR \--locale\fI " "\fILOCALE +.BR --locale\fI " "\fILOCALE Overrides the default locale (language) for client-facing error messages. .TP -.BR \--command/-c\fI " " \fICOMMAND +.BR --command/-c\fI " " \fICOMMAND Command is the operation being requested. Options are: a(ttach): Attach device d(etach): Detach device @@ -160,17 +160,17 @@ Command is the operation being requested. Options are: .IP eject, protect and unprotect are idempotent. .TP -.BR \--block-size/-b\fI " " \fIBLOCK_SIZE +.BR --block-size/-b\fI " " \fIBLOCK_SIZE The optional block size, usually 256 (SASI only), 512, 1024, 2048 or 4096 bytes. The default size for SASI hard drives is 256 bytes, for SCSI hard drives it is 512 bytes. s2p supports non-standard SCSI hard drive sector sizes as long as they are multiples of 4, but these sizes are only required for exotic platforms. .TP -.BR \--caching-mode/-m\fI " " \fICACHING_MODE +.BR --caching-mode/-m\fI " " \fICACHING_MODE Caching mode (piscsi|write-through|linux|linux-optimized), default currently is PiSCSI compatible caching. .TP -.BR \--file/-f\fI " " \fIFILE|PARAMS +.BR --file/-f\fI " " \fIFILE|PARAMS Device-specific: Either a path to a disk image file, or parameters for a non-disk device. See the s2p(1) man page for permitted file types. .TP -.BR \--type/-t\fI " " \fITYPE +.BR --type/-t\fI " " \fITYPE Specifies the device type. This type overrides the type derived from the file extension of the specified image. See the s2p(1) man page for the available device types. For some types there are shortcuts (only the first letter is required): sahd: SASI hard drive schd: SCSI hard drive @@ -181,15 +181,15 @@ Specifies the device type. This type overrides the type derived from the file ex sclp: SCSI printer schs: Host services device .TP -.BR \--name/-n\fI " " \fIVENDOR:PRODUCT:REVISION +.BR --name/-n\fI " " \fIVENDOR:PRODUCT:REVISION The optional vendor, product and revision for the device, to be returned with the SCSI INQUIRY data. A complete set of name components must be provided. VENDOR may have up to 8, PRODUCT up to 16, REVISION up to 4 characters. Padding with blanks to the maxium length is automatically applied. Once set the name of a device cannot be changed. .TP -.BR \--scsi-level\fI " " \fISCSI_LEVEL +.BR --scsi-level\fI " " \fISCSI_LEVEL The optional SCSI standard level. The default level is device-specific and usually is SCSI-2. Be careful with using this option, you will usually not need it. Explicitly setting the level may be required for drives with removable media if they are attached without providing an image file. In this case the SCSI-1-CCS level cannot be derived from the ".is1" or ".hd1" filename extension and "--scsi-level 1" can be used. .TP -.BR \-u\fI " " \fIUNIT +.BR -u\fI " " \fIUNIT Logical unit number (0-31 for SCSI, 0-1 for SASI). The LUN defaults to 0. This option is used when there are multiple SCSI/SASI devices for the same SCSI ID. .SH EXAMPLES diff --git a/doc/s2pdump.1 b/doc/s2pdump.1 index 440e6c93..f74b0712 100644 --- a/doc/s2pdump.1 +++ b/doc/s2pdump.1 @@ -36,50 +36,50 @@ Usually in order to use the generated drive image with SCSI2Pi, it should be mov .SH OPTIONS .TP -.BR \--scsi-id/-i\fI " "\fIID[:LUN] +.BR --scsi-id/-i\fI " "\fIID[:LUN] ID and optional LUN (0-31) of the SCSI target drive. .TP -.BR \--sasi-id/-h\fI " "\fIID[:LUN] +.BR --sasi-id/-h\fI " "\fIID[:LUN] ID and optional LUN (0-1) of the SASI target drive. .TP -.BR \--board-id/-B\fI " "\fIBOARD_ID +.BR --board-id/-B\fI " "\fIBOARD_ID SCSI ID of the SCSI2Pi board. If not specified ID 7 is used. The SCSI2Pi host will be functioning as the "initiator" device. Not relevant for SASI. .TP -.BR \--image-file/-f\fI " "\fIIMAGE_FILE +.BR --image-file/-f\fI " "\fIIMAGE_FILE Path to the dump/restore file. .TP -.BR \--buffer-size/-b\fI " "\fIBUFFER_SIZE +.BR --buffer-size/-b\fI " "\fIBUFFER_SIZE The transfer buffer size in bytes. The default size is 1 MiB, the minimum size is 64 KiB. .TP -.BR \-inquiry/-I\fI " "\fIID[:LUN] +.BR --inquiry/-I\fI " "\fIID[:LUN] Display INQUIRY data of ID[:LUN] and (SCSI only) device properties for use with s2p properties files. .TP -.BR \--log-level/-L\fI " " \fILOG_LEVEL +.BR --log-level/-L\fI " " \fILOG_LEVEL Set the log level (trace, debug, info, warning, error, critical, off). The default log level is 'info'. .TP -.BR \--scsi-scan/-s\fI +.BR --scsi-scan/-s\fI Scan the bus for SCSI devices and display their properties. .TP -.BR \--sasi-scan/-t\fI +.BR --sasi-scan/-t\fI Scan the bus for SASI devices and display their properties. .TP -.BR \--start-sector/-S\fI " " \fISTART +.BR --start-sector/-S\fI " " \fISTART Start sector for the dump/restore operation, the default is 0. .TP -.BR \--sector-count/-C\fI " " \fICOUNT +.BR --sector-count/-C\fI " " \fICOUNT Sector count for the dump/restore operation, the default is the drive capacity. .TP -.BR \--sasi-capacity/-c\fI " "\fISASI_CAPACITY +.BR --sasi-capacity/-c\fI " "\fISASI_CAPACITY The capacity of the SASI hard drive in sectors. This parameter only has a meaning for SASI drives. .TP -.BR \--sasi-sector-size/-z\fI " "\fISASI_SECTOR_SIZE +.BR --sasi-sector-size/-z\fI " "\fISASI_SECTOR_SIZE The sector size of the SASI hard drive in sectors (256, 512, x1024) in bytes. This parameter only has a meaning for SASI drives. .TP -.BR \--all-luns/-a\fI +.BR --all-luns/-a\fI Check all LUNs during bus scan. The default is to scan LUN 0 only. If the target device supports the REPORT LUNS SCSI command s2pdump uses this command to optimize the scan. .TP -.BR \--restore/-r\fI +.BR --restore/-r\fI Run in restore mode. The default is dump mode. .SH EXAMPLES diff --git a/doc/s2pexec.1 b/doc/s2pexec.1 index f3cc4e2e..6382cc7b 100644 --- a/doc/s2pexec.1 +++ b/doc/s2pexec.1 @@ -34,55 +34,55 @@ helps with advanced testing. The board s2pexec is running on must be a FULLSPEC .SH OPTIONS .TP -.BR \--scsi-target/-i\fI " "\fIID[:LUN] +.BR --scsi-target/-i\fI " "\fIID[:LUN] ID and optional LUN (0-31) of the SCSI target device. .TP -.BR \--sasi-target/-h\fI " "\fIID[:LUN] +.BR --sasi-target/-h\fI " "\fIID[:LUN] ID and optional LUN (0-1) of the SASI target device. .TP -.BR \--board-id/-B\fI " "\fIBOARD_ID +.BR --board-id/-B\fI " "\fIBOARD_ID Initiator ID used by s2pexec. If not specified, s2pexec will use the board (initiator) ID 7. Not relevant for SASI. .TP -.BR \--cdb/-c\fI " "\fICDB +.BR --cdb/-c\fI " "\fICDB Command block to send in hexecimal format. Hexadecimal numbers may be separated by a colon. .TP -.BR \--data/-d\fI " "\fIDATA +.BR --data/-d\fI " "\fIDATA Data to send with the command in hexedecimal format. These data are sent in the DATA OUT phase. .TP -.BR \--buffer-size/-b\fI " "\fIBUFFER_SIZE +.BR --buffer-size/-b\fI " "\fIBUFFER_SIZE Buffer size for received data, the default is 131072 bytes. When sending data the buffer is automatically sized to match the size of data to be sent. .TP -.BR \--log-level/-L\fI " " \fILOG_LEVEL +.BR --log-level/-L\fI " " \fILOG_LEVEL Set the log level (trace, debug, info, warning, error, critical, off). The default log level is 'info'. .TP -.BR \--binary-input-file/-f\fI " "\fIFILE +.BR --binary-input-file/-f\fI " "\fIFILE Optional file with binary data to send in the DATA OUT phase. .TP -.BR \--binary-output-file/-F\fI " "\fIFILE +.BR --binary-output-file/-F\fI " "\fIFILE Optional file for binary data received in the DATA IN phase. .TP -.BR \--hex-input-file/-t\fI " "\fIFILE +.BR --hex-input-file/-t\fI " "\fIFILE Optional file with data in hexadecimal (human-readable) format to send in the DATA OUT phase. .TP .BR \--hex-output-file/-T\fI " "\fIFILE Optional file for data received in the DATA IN phase. The data in this file are coded in hexadecimal (human-readable) format. .TP -.BR \--timeout/-o\fI " "\fITIMEOUT +.BR --timeout/-o\fI " "\fITIMEOUT The command timeout in seconds, default is 3 seconds. Operations like formatting may take much longer. .TP -.BR \--no-request-sense/-n\fI +.BR --no-request-sense/-n\fI Do not automatically send REQUEST SENSE after an error in order to get the sense data. .TP -.BR \--reset-bus/-r\fI +.BR --reset-bus/-r\fI Reset the bus. .TP -.BR \--hex-only\fI +.BR --hex-only\fI Do not display/save the offsets and the ASCII representantion of the received data. .TP -.BR \--help/-H\fI +.BR --help/-H\fI Show a help text. .TP -.BR \--version/-v\fI +.BR --version/-v\fI Show the s2pexec version. .SH EXIT STATUS diff --git a/doc/s2pproto.1 b/doc/s2pproto.1 index 84cfd563..2ac8e3da 100644 --- a/doc/s2pproto.1 +++ b/doc/s2pproto.1 @@ -25,37 +25,37 @@ helps with advanced testing. It requires either two connected PiSCSI/RaSCSI boar .SH OPTIONS .TP -.BR \--scsi-target/-i\fI " "\fIID[:LUN] +.BR --scsi-target/-i\fI " "\fIID[:LUN] SCSI ID and optional LUN (0-31) of the target device. .TP -.BR \--board-id/-B\fI " "\fIBOARD_ID +.BR --board-id/-B\fI " "\fIBOARD_ID SCSI ID used by s2pexec. If not specified, s2pexec will use the board (initiator) ID 7. .TP -.BR \--log-level/-L\fI " " \fILOG_LEVEL +.BR --log-level/-L\fI " " \fILOG_LEVEL Set the log level (trace, debug, info, warning, error, critical, off). The default log level is 'info'. .TP -.BR \--input-file/-f\fI " "\fIFILE +.BR --input-file/-f\fI " "\fIFILE The protobuf data input file, by default in JSON format. .TP -.BR \--output-file/-F\fI " "\fIFILE +.BR --output-file/-F\fI " "\fIFILE The protobuf data output file, by default in JSON format. If not specified JSON data will be displayed on stdout. .TP -.BR \--binary-input\fI +.BR --binary-input\fI Signals that the input file is in protobuf binary format instead of JSON. .TP -.BR \--binary-output\fI +.BR --binary-output\fI Generate a protobuf binary format output file instead of JSON for the results. .TP -.BR \--text-input\fI +.BR --text-input\fI Signals that the input file is in protobuf text format instead of JSON. .TP -.BR \--text-output\fI +.BR --text-output\fI Generate a protobuf text format file instead of JSON for the results. .TP -.BR \--help/-H\fI +.BR --help/-H\fI Show a help text. .TP -.BR \--version/-v\fI +.BR --version/-v\fI Show the s2pproto version. .SH SEE ALSO diff --git a/proto/s2p_interface.proto b/proto/s2p_interface.proto index aa15dbac..d517e270 100644 --- a/proto/s2p_interface.proto +++ b/proto/s2p_interface.proto @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- // -// SCSI target emulator and SCSI initiator tools for the Raspberry Pi +// SCSI device emulator and SCSI tools for the Raspberry Pi // // Copyright (C) 2021-2024 Uwe Seimet //