mirror of
https://github.com/akuker/RASCSI.git
synced 2026-01-23 12:16:06 +00:00
This will reduce the recurring maintenance overhead of keeping the same information up to date in multiple places Only the most common use cases are covered in the usage help text now
260 lines
6.9 KiB
C++
260 lines
6.9 KiB
C++
//---------------------------------------------------------------------------
|
|
//
|
|
// SCSI Target Emulator PiSCSI
|
|
// for Raspberry Pi
|
|
//
|
|
// Powered by XM6 TypeG Technology.
|
|
// Copyright (C) 2016-2020 GIMONS
|
|
// Copyright (C) 2021-2022 akuker
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include "scsimon/sm_core.h"
|
|
#include "hal/gpiobus.h"
|
|
#include "hal/gpiobus_factory.h"
|
|
#include "scsimon/sm_reports.h"
|
|
#include "hal/log.h"
|
|
#include "shared/piscsi_version.h"
|
|
#include "shared/piscsi_util.h"
|
|
#include <climits>
|
|
#include <csignal>
|
|
#include <getopt.h>
|
|
#include <iostream>
|
|
#include <sched.h>
|
|
#include <sys/time.h>
|
|
|
|
using namespace std;
|
|
using namespace piscsi_util;
|
|
|
|
void ScsiMon::KillHandler(int)
|
|
{
|
|
running = false;
|
|
}
|
|
|
|
void ScsiMon::ParseArguments(const vector<char *> &args)
|
|
{
|
|
int opt;
|
|
|
|
while ((opt = getopt(static_cast<int>(args.size()), args.data(), "-Hhb:i:")) != -1) {
|
|
switch (opt) {
|
|
// The three options below are kind of a compound option with two letters
|
|
case 'h':
|
|
case 'H':
|
|
print_help = true;
|
|
break;
|
|
case 'b':
|
|
buff_size = atoi(optarg);
|
|
break;
|
|
case 'i':
|
|
input_file_name = optarg;
|
|
import_data = true;
|
|
break;
|
|
case 1:
|
|
file_base_name = optarg;
|
|
break;
|
|
default:
|
|
cout << "default: " << optarg << endl;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Process any remaining command line arguments (not options). */
|
|
if (optind < static_cast<int>(args.size())) {
|
|
while (optind < static_cast<int>(args.size())) {
|
|
file_base_name = args[optind];
|
|
optind++;
|
|
}
|
|
}
|
|
|
|
vcd_file_name = file_base_name;
|
|
vcd_file_name += ".vcd";
|
|
json_file_name = file_base_name;
|
|
json_file_name += ".json";
|
|
html_file_name = file_base_name;
|
|
html_file_name += ".html";
|
|
}
|
|
|
|
void ScsiMon::PrintHelpText(const vector<char *> &args) const
|
|
{
|
|
spdlog::info("SCSI Target Emulator PiSCSI (SCSI Monitor Capture Tool)\n\n");
|
|
spdlog::info("Usage: " + string(args[0]) + " -i [input file json] -b [buffer size] [output file]\n\n");
|
|
}
|
|
|
|
void ScsiMon::Banner() const
|
|
{
|
|
if (import_data) {
|
|
spdlog::info("Reading input file: " + input_file_name);
|
|
} else {
|
|
spdlog::info("Reading live data from the GPIO pins");
|
|
spdlog::info(" Connection type: " + CONNECT_DESC);
|
|
}
|
|
spdlog::info(" Data buffer size: " + to_string(buff_size));
|
|
spdlog::info(" ");
|
|
spdlog::info("Generating output files:");
|
|
spdlog::info(" " + vcd_file_name + " - Value Change Dump file that can be opened with GTKWave");
|
|
spdlog::info(" " + json_file_name + " - JSON file with raw data");
|
|
spdlog::info(" " + html_file_name + " - HTML file with summary of commands");
|
|
}
|
|
|
|
bool ScsiMon::Init()
|
|
{
|
|
// Interrupt handler settings
|
|
if (signal(SIGINT, KillHandler) == SIG_ERR) {
|
|
return false;
|
|
}
|
|
if (signal(SIGHUP, KillHandler) == SIG_ERR) {
|
|
return false;
|
|
}
|
|
if (signal(SIGTERM, KillHandler) == SIG_ERR) {
|
|
return false;
|
|
}
|
|
|
|
bus = GPIOBUS_Factory::Create(BUS::mode_e::TARGET);
|
|
if (bus == nullptr) {
|
|
spdlog::error("Unable to intiailize the GPIO bus. Exiting....");
|
|
return false;
|
|
}
|
|
|
|
running = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void ScsiMon::Cleanup() const
|
|
{
|
|
if (!import_data) {
|
|
spdlog::info("Stopping data collection ...");
|
|
}
|
|
spdlog::info(" ");
|
|
spdlog::info("Generating " + vcd_file_name + "...");
|
|
scsimon_generate_value_change_dump(vcd_file_name, data_buffer);
|
|
spdlog::info("Generating " + json_file_name + "...");
|
|
scsimon_generate_json(json_file_name, data_buffer);
|
|
spdlog::info("Generating " + html_file_name + "...");
|
|
scsimon_generate_html(html_file_name, data_buffer);
|
|
|
|
bus->Cleanup();
|
|
}
|
|
|
|
void ScsiMon::Reset() const
|
|
{
|
|
bus->Reset();
|
|
}
|
|
|
|
int ScsiMon::run(const vector<char *> &args)
|
|
{
|
|
#ifdef DEBUG
|
|
spdlog::set_level(spdlog::level::trace);
|
|
#else
|
|
spdlog::set_level(spdlog::level::info);
|
|
#endif
|
|
spdlog::set_pattern("%^[%l]%$ %v");
|
|
|
|
piscsi_util::Banner("(SCSI Monitor Capture Tool)");
|
|
|
|
ParseArguments(args);
|
|
|
|
shared_ptr<DataSample> prev_sample = nullptr;
|
|
shared_ptr<DataSample> this_sample = nullptr;
|
|
timeval start_time;
|
|
timeval stop_time;
|
|
uint64_t loop_count = 0;
|
|
timeval time_diff;
|
|
uint64_t elapsed_us;
|
|
|
|
if (print_help) {
|
|
PrintHelpText(args);
|
|
exit(0);
|
|
}
|
|
|
|
Banner();
|
|
|
|
data_buffer.reserve(buff_size);
|
|
|
|
if (import_data) {
|
|
data_idx = scsimon_read_json(input_file_name, data_buffer);
|
|
if (data_idx > 0) {
|
|
spdlog::debug("Read " + to_string(data_idx) + " samples from '" + input_file_name + "'");
|
|
Cleanup();
|
|
}
|
|
exit(0);
|
|
}
|
|
|
|
spdlog::info(" ");
|
|
spdlog::info("Now collecting data.... Press CTRL-C to stop.");
|
|
spdlog::info(" ");
|
|
|
|
// Initialize
|
|
int ret = 0;
|
|
if (!Init()) {
|
|
ret = EPERM;
|
|
goto init_exit;
|
|
}
|
|
|
|
// Reset
|
|
Reset();
|
|
|
|
#ifdef __linux__
|
|
// Set the affinity to a specific processor core
|
|
FixCpu(3);
|
|
|
|
// Scheduling policy setting (highest priority)
|
|
struct sched_param schparam;
|
|
schparam.sched_priority = sched_get_priority_max(SCHED_FIFO);
|
|
sched_setscheduler(0, SCHED_FIFO, &schparam);
|
|
#endif
|
|
|
|
// Start execution
|
|
running = true;
|
|
bus->SetACK(false);
|
|
|
|
(void)gettimeofday(&start_time, nullptr);
|
|
|
|
// Main Loop
|
|
while (running) {
|
|
loop_count++;
|
|
if (loop_count > LLONG_MAX - 1) {
|
|
spdlog::info("Maximum amount of time has elapsed. SCSIMON is terminating.");
|
|
running = false;
|
|
}
|
|
|
|
if (data_idx >= (buff_size - 2)) {
|
|
spdlog::info("Internal data buffer is full. SCSIMON is terminating.");
|
|
running = false;
|
|
}
|
|
|
|
this_sample = bus->GetSample(loop_count);
|
|
if ((prev_sample == nullptr) || (this_sample->GetRawCapture() != prev_sample->GetRawCapture())) {
|
|
data_buffer.push_back(this_sample);
|
|
data_idx++;
|
|
prev_sample = this_sample;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
// Collect one last sample, otherwise it looks like the end of the data was cut off
|
|
if (data_idx < buff_size) {
|
|
data_buffer.push_back(bus->GetSample(loop_count));
|
|
data_idx++;
|
|
}
|
|
|
|
(void)gettimeofday(&stop_time, nullptr);
|
|
|
|
timersub(&stop_time, &start_time, &time_diff);
|
|
|
|
elapsed_us = ((time_diff.tv_sec * 1000000) + time_diff.tv_usec);
|
|
spdlog::info("Elapsed time: " + to_string(elapsed_us) + " microseconds (" + to_string(elapsed_us / 1000000) +
|
|
" seconds");
|
|
spdlog::info("Collected " + to_string(data_idx) + " changes");
|
|
|
|
ns_per_loop = (double)(elapsed_us * 1000) / (double)loop_count;
|
|
spdlog::info("Read the SCSI bus " + to_string(loop_count) + " times with an average of " +
|
|
to_string(ns_per_loop) + " ns for each read");
|
|
|
|
Cleanup();
|
|
|
|
init_exit:
|
|
exit(ret);
|
|
}
|