From cd319b6aa11ff8ab179b342d2731741960df5f8a Mon Sep 17 00:00:00 2001 From: Daniel Markstedt Date: Sun, 2 Aug 2026 22:35:53 +0200 Subject: [PATCH] scsiloop: gate hardware diagnostics by platform Replace getopt() and usleep() dependencies with portable C++ parsing and sleeps. Refuse to execute the GPIO loopback diagnostic off Linux/Raspberry Pi hardware, and omit the hardware-only target from non-Linux Meson builds. --- cpp/meson.build | 4 +- cpp/scsiloop/scsiloop_core.cpp | 70 +++++++++++++++++++++------------ cpp/scsiloop/scsiloop_gpio.cpp | 18 +++++---- cpp/scsiloop/scsiloop_timer.cpp | 4 +- 4 files changed, 61 insertions(+), 35 deletions(-) diff --git a/cpp/meson.build b/cpp/meson.build index d362493dd1..8f7af04065 100644 --- a/cpp/meson.build +++ b/cpp/meson.build @@ -162,12 +162,14 @@ if to_build.contains('scsimon') ) endif -if to_build.contains('scsiloop') +if to_build.contains('scsiloop') and host_machine.system() == 'linux' scsiloop_bin = executable('scsiloop', scsiloop_src + shared_src, dependencies : common_deps, install : true, ) +elif to_build.contains('scsiloop') + message('Skipping scsiloop: it requires Linux on Raspberry Pi GPIO hardware') endif if connect_type == 'FULLSPEC' and to_build.contains('scsidump') diff --git a/cpp/scsiloop/scsiloop_core.cpp b/cpp/scsiloop/scsiloop_core.cpp index c5806bf2f6..7aa8bce1fc 100644 --- a/cpp/scsiloop/scsiloop_core.cpp +++ b/cpp/scsiloop/scsiloop_core.cpp @@ -13,6 +13,7 @@ //--------------------------------------------------------------------------- #include "hal/log.h" +#include "hal/sbc_version.h" #include "shared/piscsi_version.h" #include "shared/piscsi_util.h" #include "spdlog/sinks/stdout_color_sinks.h" @@ -22,7 +23,10 @@ #include "scsiloop/scsiloop_gpio.h" #include "scsiloop/scsiloop_timer.h" +#include +#include #include +#include #include #if defined CONNECT_TYPE_STANDARD @@ -42,6 +46,25 @@ using namespace spdlog; string current_log_level = "unknown"; // Some versions of spdlog do not support get_log_level() +namespace { + +bool IsSupportedPlatform() +{ +#if defined(__linux__) + try { + SBC_Version::Init(); + return SBC_Version::IsRaspberryPi(); + } + catch (const invalid_argument&) { + return false; + } +#else + return false; +#endif +} + +} + void ScsiLoop::Banner(const vector &args) const { cout << piscsi_util::Banner("(SCSI Loopback Test)"); @@ -87,34 +110,26 @@ void ScsiLoop::TerminationHandler(int signum) bool ScsiLoop::ParseArgument(const vector &args) { - string name; string log_level; - const char *locale = setlocale(LC_MESSAGES, ""); - if (locale == nullptr || !strcmp(locale, "C")) { - locale = "en"; - } - - opterr = 1; - int opt; - - while ((opt = getopt(static_cast(args.size()), args.data(), "-L:")) != -1) { - switch (opt) { - case 'L': - log_level = optarg; - continue; - - default: - return false; - } - - if (optopt) { - return false; - } - } + for (size_t i = 1; i < args.size(); ++i) { + const string_view argument(args[i]); + if (argument == "-L") { + if (++i == args.size()) { + return false; + } + log_level = args[i]; + } + else if (argument.starts_with("-L") && argument.size() > 2) { + log_level = argument.substr(2); + } + else { + return false; + } + } if (!log_level.empty()) { - SetLogLevel(log_level); + return SetLogLevel(log_level); } return true; @@ -131,12 +146,17 @@ int ScsiLoop::run(const vector &args) // ParseArgument() requires the bus to have been initialized first, which requires the root user. // The -v option should be available for any user, which requires special handling. for (auto this_arg : args) { - if (!strcasecmp(this_arg, "-v")) { + if (!strcmp(this_arg, "-v")) { cout << piscsi_get_version_string() << endl; return 0; } } + if (!IsSupportedPlatform()) { + cerr << "scsiloop requires Linux on a Raspberry Pi with access to the GPIO hardware." << endl; + return EXIT_FAILURE; + } + // Create a thread-safe stdout logger to process the log messages const auto logger = stdout_color_mt("scsiloop stdout logger"); set_level(level::info); diff --git a/cpp/scsiloop/scsiloop_gpio.cpp b/cpp/scsiloop/scsiloop_gpio.cpp index 9a803ce2e0..b2a9dce9eb 100644 --- a/cpp/scsiloop/scsiloop_gpio.cpp +++ b/cpp/scsiloop/scsiloop_gpio.cpp @@ -13,6 +13,8 @@ #include "hal/sbc_version.h" #include "scsiloop/scsiloop_cout.h" #include "hal/log.h" +#include +#include #if defined CONNECT_TYPE_STANDARD #include "hal/connection_type/connection_standard.h" @@ -221,7 +223,7 @@ int ScsiLoop_GPIO::test_gpio_pin(loopback_connection &gpio_rec, vector & LOGTRACE("dir ctrl pin: %d", (int)gpio_rec.dir_ctrl_pin); set_output_channel(gpio_rec.dir_ctrl_pin); - usleep(sleep_time); + this_thread::sleep_for(chrono::microseconds(sleep_time)); // Set all GPIOs high (initialize to a known state) for (auto cur_gpio : loopback_conn_table) { @@ -229,7 +231,7 @@ int ScsiLoop_GPIO::test_gpio_pin(loopback_connection &gpio_rec, vector & bus->SetMode(cur_gpio.this_pin, GPIO_INPUT); } - usleep(sleep_time); + this_thread::sleep_for(chrono::microseconds(sleep_time)); bus->Acquire(); // ############################################ @@ -237,7 +239,7 @@ int ScsiLoop_GPIO::test_gpio_pin(loopback_connection &gpio_rec, vector & bus->SetMode(gpio_rec.this_pin, GPIO_OUTPUT); bus->SetSignal(gpio_rec.this_pin, ON); - usleep(sleep_time); + this_thread::sleep_for(chrono::microseconds(sleep_time)); bus->Acquire(); // loop through all of the gpios @@ -281,7 +283,7 @@ int ScsiLoop_GPIO::test_gpio_pin(loopback_connection &gpio_rec, vector & // set the transceivers to input set_output_channel(-1); - usleep(sleep_time); + this_thread::sleep_for(chrono::microseconds(sleep_time)); bus->Acquire(); // # loop through all of the gpios @@ -311,14 +313,14 @@ int ScsiLoop_GPIO::test_gpio_pin(loopback_connection &gpio_rec, vector & // Set the transceiver back to output set_output_channel(gpio_rec.dir_ctrl_pin); - usleep(sleep_time); + this_thread::sleep_for(chrono::microseconds(sleep_time)); // ############################################# // set the test gpio high bus->SetMode(gpio_rec.this_pin, GPIO_OUTPUT); bus->SetSignal(gpio_rec.this_pin, OFF); - usleep(sleep_time); + this_thread::sleep_for(chrono::microseconds(sleep_time)); bus->Acquire(); // loop through all of the gpios @@ -458,7 +460,7 @@ int ScsiLoop_GPIO::RunDataInputTest(vector &error_list) for (uint32_t val = 0; val < UINT8_MAX; val++) { set_dat_inputs_loop(val); - usleep(delay_time_us); + this_thread::sleep_for(chrono::microseconds(delay_time_us)); bus->Acquire(); uint8_t read_val = bus->GetDAT(); @@ -488,7 +490,7 @@ int ScsiLoop_GPIO::RunDataOutputTest(vector &error_list) for (uint32_t val = 0; val < UINT8_MAX; val++) { bus->SetDAT(val); - usleep(delay_time_us); + this_thread::sleep_for(chrono::microseconds(delay_time_us)); bus->Acquire(); uint8_t read_val = get_dat_outputs_loop(); diff --git a/cpp/scsiloop/scsiloop_timer.cpp b/cpp/scsiloop/scsiloop_timer.cpp index 4cfe627a3d..738b6239d7 100644 --- a/cpp/scsiloop/scsiloop_timer.cpp +++ b/cpp/scsiloop/scsiloop_timer.cpp @@ -11,6 +11,8 @@ #include "hal/systimer.h" #include "scsiloop/scsiloop_cout.h" #include "hal/log.h" +#include +#include int ScsiLoop_Timer::RunTimerTest(vector &error_list) { @@ -27,7 +29,7 @@ int ScsiLoop_Timer::RunTimerTest(vector &error_list) LOGDEBUG("++ Testing SysTimer::GetTimerLow()") uint32_t before = SysTimer::GetTimerLow(); for (int i = 0; i < 10; i++) { - usleep(100000); + this_thread::sleep_for(chrono::microseconds(100000)); ScsiLoop_Cout::PrintUpdate(); } uint32_t after = SysTimer::GetTimerLow();