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