Added rascsi filter to limit logging to a particular device (#978)

* Support for ID and LUN parameter for the -L option in rascsi

* Added DeviceLogger class

* Removed dupiicate code

* Fixed SonarQube issues

* Added unit tests, improved code sharing

* Fixed regression (#979)
This commit is contained in:
Uwe Seimet 2022-11-11 21:08:48 +01:00 committed by GitHub
parent 4fa513090a
commit 454c61ac0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 673 additions and 409 deletions

View File

@ -7,7 +7,6 @@
//
//---------------------------------------------------------------------------
#include "shared/log.h"
#include "shared/rascsi_exceptions.h"
#include "devices/primary_device.h"
#include "abstract_controller.h"
@ -104,7 +103,6 @@ void AbstractController::ProcessPhase()
break;
default:
LOGERROR("Cannot process phase %s", BUS::GetPhaseStrRaw(GetPhase()))
throw scsi_exception(sense_key::ABORTED_COMMAND);
break;
}
@ -136,7 +134,7 @@ bool AbstractController::HasDeviceForLun(int lun) const
int AbstractController::ExtractInitiatorId(int id_data) const
{
int initiator_id = -1;
int initiator_id = UNKNOWN_INITIATOR_ID;
if (int tmp = id_data - (1 << target_id); tmp) {
initiator_id = 0;

View File

@ -28,6 +28,8 @@ class AbstractController : public PhaseHandler, public enable_shared_from_this<A
{
public:
static inline const int UNKNOWN_INITIATOR_ID = -1;
enum class rascsi_shutdown_mode {
NONE,
STOP_RASCSI,

View File

@ -6,13 +6,13 @@
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker
// Copyright (C) 2022 Uwe Seimet
//
// Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder.
//
//---------------------------------------------------------------------------
#include "shared/log.h"
#include "shared/rascsi_exceptions.h"
#include "hal/gpiobus.h"
#include "hal/systimer.h"
@ -31,6 +31,8 @@ using namespace scsi_defs;
ScsiController::ScsiController(shared_ptr<ControllerManager> controller_manager, int target_id)
: AbstractController(controller_manager, target_id, LUN_MAX)
{
logger.SetIdAndLun(target_id, -1);
// The initial buffer size will default to either the default buffer size OR
// the size of an Ethernet message, whichever is larger.
AllocateBuffer(std::max(DEFAULT_BUFFER_SIZE, ETH_FRAME_LEN + 16 + ETH_FCS_LEN));
@ -55,7 +57,7 @@ BUS::phase_t ScsiController::Process(int id)
GetBus().Acquire();
if (GetBus().GetRST()) {
LOGWARN("RESET signal received!")
logger.Warn("RESET signal received!");
Reset();
@ -64,13 +66,6 @@ BUS::phase_t ScsiController::Process(int id)
return GetPhase();
}
if (id != UNKNOWN_INITIATOR_ID) {
LOGTRACE("%s Initiator ID is %d", __PRETTY_FUNCTION__, id)
}
else {
LOGTRACE("%s Initiator ID is unknown", __PRETTY_FUNCTION__)
}
initiator_id = id;
try {
@ -78,7 +73,7 @@ BUS::phase_t ScsiController::Process(int id)
}
catch(const scsi_exception&) {
// Any exception should have been handled during the phase processing
LOGERROR("%s Unhandled SCSI error, resetting controller and bus and entering bus free phase", __PRETTY_FUNCTION__)
logger.Error("Unhandled SCSI error, resetting controller and bus and entering bus free phase");
Reset();
GetBus().Reset();
@ -92,7 +87,7 @@ BUS::phase_t ScsiController::Process(int id)
void ScsiController::BusFree()
{
if (!IsBusFree()) {
LOGTRACE("%s Bus free phase", __PRETTY_FUNCTION__)
logger.Trace("Bus free phase");
SetPhase(BUS::phase_t::busfree);
@ -124,21 +119,21 @@ void ScsiController::BusFree()
// This code has to be executed in the bus free phase and thus has to be located in the controller.
switch(shutdown_mode) {
case rascsi_shutdown_mode::STOP_RASCSI:
LOGINFO("RaSCSI shutdown requested")
logger.Info("RaSCSI shutdown requested");
exit(EXIT_SUCCESS);
break;
case rascsi_shutdown_mode::STOP_PI:
LOGINFO("Raspberry Pi shutdown requested")
logger.Info("Raspberry Pi shutdown requested");
if (system("init 0") == -1) {
LOGERROR("Raspberry Pi shutdown failed: %s", strerror(errno))
logger.Error("Raspberry Pi shutdown failed: " + string(strerror(errno)));
}
break;
case rascsi_shutdown_mode::RESTART_PI:
LOGINFO("Raspberry Pi restart requested")
logger.Info("Raspberry Pi restart requested");
if (system("init 6") == -1) {
LOGERROR("Raspberry Pi restart failed: %s", strerror(errno))
logger.Error("Raspberry Pi restart failed: " + string(strerror(errno)));
}
break;
@ -168,7 +163,7 @@ void ScsiController::Selection()
return;
}
LOGTRACE("%s Selection Phase Target ID=%d", __PRETTY_FUNCTION__, GetTargetId())
logger.Trace("Selection phase");
SetPhase(BUS::phase_t::selection);
@ -191,7 +186,7 @@ void ScsiController::Selection()
void ScsiController::Command()
{
if (!IsCommand()) {
LOGTRACE("%s Command Phase", __PRETTY_FUNCTION__)
logger.Trace("Command phase");
SetPhase(BUS::phase_t::command);
@ -201,7 +196,9 @@ void ScsiController::Command()
const int actual_count = GetBus().CommandHandShake(GetBuffer());
if (actual_count == 0) {
LOGTRACE("ID %d LUN %d received unknown command: $%02X", GetTargetId(), GetEffectiveLun(), GetBuffer()[0])
stringstream s;
s << "Received unknown command: $" << setfill('0') << setw(2) << hex << GetBuffer()[0];
logger.Trace(s.str());
Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
return;
@ -211,21 +208,19 @@ void ScsiController::Command()
// If not able to receive all, move to the status phase
if (actual_count != command_byte_count) {
LOGERROR("Command byte count mismatch for command $%02X: expected %d bytes, received %d byte(s)",
GetBuffer()[0], command_byte_count, actual_count)
stringstream s;
s << "Command byte count mismatch for command $" << setfill('0') << setw(2) << hex << GetBuffer()[0];
logger.Error(s.str() + ": expected " + to_string(command_byte_count) + " bytes, received"
+ to_string(actual_count) + " byte(s)");
Error(sense_key::ABORTED_COMMAND);
return;
}
AllocateCmd(command_byte_count);
// Command data transfer
stringstream s;
AllocateCmd(command_byte_count);
for (int i = 0; i < command_byte_count; i++) {
GetCmd()[i] = GetBuffer()[i];
s << setfill('0') << setw(2) << hex << GetCmd(i);
}
LOGTRACE("%s CDB=$%s",__PRETTY_FUNCTION__, s.str().c_str())
SetLength(0);
@ -235,7 +230,13 @@ void ScsiController::Command()
void ScsiController::Execute()
{
LOGDEBUG("++++ CMD ++++ Executing command $%02X", static_cast<int>(GetOpcode()))
stringstream s;
s << "Controller is executing " << command_mapping.find(GetOpcode())->second.second << ", CDB $"
<< setfill('0') << hex;
for (int i = 0; i < BUS::GetCommandByteCount(static_cast<uint8_t>(GetOpcode())); i++) {
s << setw(2) << GetCmd(i);
}
logger.Debug(s.str());
// Initialization for data transfer
ResetOffset();
@ -250,7 +251,7 @@ void ScsiController::Execute()
int lun = GetEffectiveLun();
if (!HasDeviceForLun(lun)) {
if (GetOpcode() != scsi_command::eCmdInquiry && GetOpcode() != scsi_command::eCmdRequestSense) {
LOGTRACE("Invalid LUN %d for device ID %d", lun, GetTargetId())
logger.Trace("Invalid LUN " + to_string(lun));
Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_LUN);
@ -264,7 +265,7 @@ void ScsiController::Execute()
// SCSI-2 4.4.3 Incorrect logical unit handling
if (GetOpcode() == scsi_command::eCmdInquiry && !HasDeviceForLun(lun)) {
LOGTRACE("Reporting LUN %d for device ID %d as not supported", GetEffectiveLun(), GetTargetId())
logger.Trace("Reporting LUN" + to_string(GetEffectiveLun()) + " as not supported");
GetBuffer().data()[0] = 0x7f;
@ -295,13 +296,16 @@ void ScsiController::Status()
{
if (!IsStatus()) {
// Minimum execution time
// TODO Why is a delay needed? Is this covered by the SCSI specification?
if (execstart > 0) {
Sleep();
} else {
SysTimer::SleepUsec(5);
}
LOGTRACE("%s Status Phase, status is $%02X",__PRETTY_FUNCTION__, static_cast<int>(GetStatus()))
stringstream s;
s << "Status Phase, status is $" << setfill('0') << setw(2) << hex << static_cast<int>(GetStatus());
logger.Trace(s.str());
SetPhase(BUS::phase_t::status);
@ -325,7 +329,7 @@ void ScsiController::Status()
void ScsiController::MsgIn()
{
if (!IsMsgIn()) {
LOGTRACE("%s Message In phase", __PRETTY_FUNCTION__)
logger.Trace("Message In phase");
SetPhase(BUS::phase_t::msgin);
@ -342,10 +346,8 @@ void ScsiController::MsgIn()
void ScsiController::MsgOut()
{
LOGTRACE("%s ID %d",__PRETTY_FUNCTION__, GetTargetId())
if (!IsMsgOut()) {
LOGTRACE("Message Out Phase")
logger.Trace("Message Out phase");
// process the IDENTIFY message
if (IsSelection()) {
@ -385,7 +387,7 @@ void ScsiController::DataIn()
return;
}
LOGTRACE("%s Going into Data-in Phase", __PRETTY_FUNCTION__)
logger.Trace("Entering Data In phase");
SetPhase(BUS::phase_t::datain);
@ -415,7 +417,7 @@ void ScsiController::DataOut()
return;
}
LOGTRACE("%s Data out phase", __PRETTY_FUNCTION__)
logger.Trace("Data Out phase");
SetPhase(BUS::phase_t::dataout);
@ -453,7 +455,7 @@ void ScsiController::Error(sense_key sense_key, asc asc, status status)
int lun = GetEffectiveLun();
if (!HasDeviceForLun(lun) || asc == asc::INVALID_LUN) {
if (!HasDeviceForLun(0)) {
LOGERROR("No LUN 0 for device %d", GetTargetId())
logger.Error("No LUN 0");
SetStatus(status);
SetMessage(0x00);
@ -467,7 +469,10 @@ void ScsiController::Error(sense_key sense_key, asc asc, status status)
}
if (sense_key != sense_key::NO_SENSE || asc != asc::NO_ADDITIONAL_SENSE_INFORMATION) {
LOGDEBUG("Error status: Sense Key $%02X, ASC $%02X", static_cast<int>(sense_key), static_cast<int>(asc))
stringstream s;
s << setfill('0') << setw(2) << hex << "Error status: Sense Key $" << static_cast<int>(sense_key)
<< ", ASC $" << static_cast<int>(asc);
logger.Debug(s.str());
// Set Sense Key and ASC for a subsequent REQUEST SENSE
GetDeviceForLun(lun)->SetStatusCode((static_cast<int>(sense_key) << 16) | (static_cast<int>(asc) << 8));
@ -476,7 +481,7 @@ void ScsiController::Error(sense_key sense_key, asc asc, status status)
SetStatus(status);
SetMessage(0x00);
LOGTRACE("%s Error (to status phase)", __PRETTY_FUNCTION__)
logger.Trace("Error (to status phase)");
Status();
}
@ -487,8 +492,7 @@ void ScsiController::Send()
assert(GetBus().GetIO());
if (HasValidLength()) {
LOGTRACE("%s%s", __PRETTY_FUNCTION__, (" Sending handhake with offset " + to_string(GetOffset()) + ", length "
+ to_string(GetLength())).c_str())
logger.Trace("Sending data, offset: " + to_string(GetOffset()) + ", length: " + to_string(GetLength()));
// The delay should be taken from the respective LUN, but as there are no Daynaport drivers for
// LUNs other than 0 this work-around works.
@ -505,7 +509,6 @@ void ScsiController::Send()
return;
}
// Block subtraction, result initialization
DecrementBlocks();
bool result = true;
@ -513,7 +516,7 @@ void ScsiController::Send()
if (IsDataIn() && GetBlocks() != 0) {
// set next buffer (set offset, length)
result = XferIn(GetBuffer());
LOGTRACE("%s%s", __PRETTY_FUNCTION__, (" Processing after data collection. Blocks: " + to_string(GetBlocks())).c_str())
logger.Trace("Processing after data collection. Blocks: " + to_string(GetBlocks()));
}
// If result FALSE, move to status phase
@ -524,14 +527,14 @@ void ScsiController::Send()
// Continue sending if block !=0
if (GetBlocks() != 0){
LOGTRACE("%s%s", __PRETTY_FUNCTION__, (" Continuing to send. Blocks: " + to_string(GetBlocks())).c_str())
logger.Trace("Continuing to send. Blocks: " + to_string(GetBlocks()));
assert(HasValidLength());
assert(GetOffset() == 0);
return;
}
// Move to next phase
LOGTRACE("%s Move to next phase: %s", __PRETTY_FUNCTION__, BUS::GetPhaseStrRaw(GetPhase()))
logger.Trace("Moving to next phase: " + string(BUS::GetPhaseStrRaw(GetPhase())));
switch (GetPhase()) {
// Message in phase
case BUS::phase_t::msgin:
@ -571,40 +574,36 @@ void ScsiController::Send()
void ScsiController::Receive()
{
assert(!GetBus().GetREQ());
assert(!GetBus().GetIO());
if (HasValidLength()) {
logger.Trace("Receiving data, transfer length: " + to_string(GetLength()) + " byte(s)");
// If not able to receive all, move to status phase
if (uint32_t len = GetBus().ReceiveHandShake(GetBuffer().data() + GetOffset(), GetLength()); len != GetLength()) {
logger.Error("Not able to receive " + to_string(GetLength()) + " byte(s) of data, only received "
+ to_string(len));
Error(sense_key::ABORTED_COMMAND);
return;
}
}
if (IsByteTransfer()) {
ReceiveBytes();
return;
}
LOGTRACE("%s",__PRETTY_FUNCTION__)
// REQ is low
assert(!GetBus().GetREQ());
assert(!GetBus().GetIO());
// Length != 0 if received
if (HasValidLength()) {
LOGTRACE("%s Length is %d byte(s)", __PRETTY_FUNCTION__, GetLength())
// If not able to receive all, move to status phase
if (int len = GetBus().ReceiveHandShake(GetBuffer().data() + GetOffset(), GetLength());
len != static_cast<int>(GetLength())) {
LOGERROR("%s Not able to receive %d byte(s) of data, only received %d",__PRETTY_FUNCTION__, GetLength(), len)
Error(sense_key::ABORTED_COMMAND);
return;
}
UpdateOffsetAndLength();
return;
}
// Block subtraction, result initialization
DecrementBlocks();
bool result = true;
// Processing after receiving data (by phase)
LOGTRACE("%s Phase: %s",__PRETTY_FUNCTION__, BUS::GetPhaseStrRaw(GetPhase()))
logger.Trace("Phase: " + string(BUS::GetPhaseStrRaw(GetPhase())));
switch (GetPhase()) {
case BUS::phase_t::dataout:
if (GetBlocks() == 0) {
@ -684,32 +683,16 @@ bool ScsiController::XferMsg(int msg)
void ScsiController::ReceiveBytes()
{
assert(!GetBus().GetREQ());
assert(!GetBus().GetIO());
if (HasValidLength()) {
LOGTRACE("%s Length is %d bytes", __PRETTY_FUNCTION__, GetLength())
// If not able to receive all, move to status phase
if (uint32_t len = GetBus().ReceiveHandShake(GetBuffer().data() + GetOffset(), GetLength()); len != GetLength()) {
LOGERROR("%s Not able to receive %d byte(s) of data, only received %d",
__PRETTY_FUNCTION__, GetLength(), len)
Error(sense_key::ABORTED_COMMAND);
return;
}
SetBytesToTransfer(GetLength());
UpdateOffsetAndLength();
return;
}
// Result initialization
bool result = true;
// Processing after receiving data (by phase)
LOGTRACE("%s Phase: %s",__PRETTY_FUNCTION__, BUS::GetPhaseStrRaw(GetPhase()))
logger.Trace("Phase: " + string(BUS::GetPhaseStrRaw(GetPhase())));
switch (GetPhase()) {
case BUS::phase_t::dataout:
result = XferOut(false);
@ -804,7 +787,10 @@ void ScsiController::DataOutNonBlockOriented()
break;
default:
LOGWARN("Unexpected Data Out phase for command $%02X", static_cast<int>(GetOpcode()))
stringstream s;
s << "Unexpected Data Out phase for command $" << setfill('0') << setw(2) << hex
<< static_cast<int>(GetOpcode());
logger.Warn(s.str());
break;
}
}
@ -819,7 +805,9 @@ bool ScsiController::XferIn(vector<uint8_t>& buf)
{
assert(IsDataIn());
LOGTRACE("%s command=%02X", __PRETTY_FUNCTION__, static_cast<int>(GetOpcode()))
stringstream s;
s << "Command: $" << setfill('0') << setw(2) << hex << static_cast<int>(GetOpcode());
logger.Trace(s.str());
int lun = GetEffectiveLun();
if (!HasDeviceForLun(lun)) {
@ -930,11 +918,14 @@ bool ScsiController::XferOutBlockOriented(bool cont)
}
case scsi_command::eCmdSetMcastAddr:
LOGTRACE("%s Done with DaynaPort Set Multicast Address", __PRETTY_FUNCTION__)
logger.Trace("Done with DaynaPort Set Multicast Address");
break;
default:
LOGWARN("Received an unexpected command ($%02X) in %s", static_cast<int>(GetOpcode()), __PRETTY_FUNCTION__)
stringstream s;
s << "Received an unexpected command ($" << setfill('0') << setw(2) << hex
<< static_cast<int>(GetOpcode()) << ")";
logger.Warn(s.str());
break;
}
@ -946,12 +937,12 @@ void ScsiController::ProcessCommand()
uint32_t len = GPIOBUS::GetCommandByteCount(GetBuffer()[0]);
stringstream s;
s << setfill('0') << setw(2) << hex;
s << "CDB=$" << setfill('0') << setw(2) << hex;
for (uint32_t i = 0; i < len; i++) {
GetCmd()[i] = GetBuffer()[i];
s << GetCmd(i);
}
LOGTRACE("%s CDB=$%s",__PRETTY_FUNCTION__, s.str().c_str())
logger.Trace(s.str());
Execute();
}
@ -963,13 +954,13 @@ void ScsiController::ParseMessage()
const uint8_t message_type = scsi.msb[i];
if (message_type == 0x06) {
LOGTRACE("Received ABORT message")
logger.Trace("Received ABORT message");
BusFree();
return;
}
if (message_type == 0x0C) {
LOGTRACE("Received BUS DEVICE RESET message")
logger.Trace("Received BUS DEVICE RESET message");
scsi.syncoffset = 0;
if (auto device = GetDeviceForLun(identified_lun); device != nullptr) {
device->DiscardReservation();
@ -980,11 +971,11 @@ void ScsiController::ParseMessage()
if (message_type >= 0x80) {
identified_lun = static_cast<int>(message_type) & 0x1F;
LOGTRACE("Received IDENTIFY message for LUN %d", identified_lun)
logger.Trace("Received IDENTIFY message for LUN " + to_string(identified_lun));
}
if (message_type == 0x01) {
LOGTRACE("Received EXTENDED MESSAGE")
logger.Trace("Received EXTENDED MESSAGE");
// Check only when synchronous transfer is possible
if (!scsi.syncenable || scsi.msb[i + 2] != 0x01) {

View File

@ -16,6 +16,7 @@
#include "shared/scsi.h"
#include "controller_manager.h"
#include "devices/device_logger.h"
#include "abstract_controller.h"
#include <array>
@ -34,8 +35,6 @@ class ScsiController : public AbstractController
// REQ/ACK offset(limited to 16)
static const uint8_t MAX_SYNC_OFFSET = 16;
static const int UNKNOWN_INITIATOR_ID = -1;
const int DEFAULT_BUFFER_SIZE = 0x1000;
using scsi_t = struct _scsi_t {
@ -88,6 +87,8 @@ public:
private:
DeviceLogger logger;
// Execution start time
uint32_t execstart = 0;

View File

@ -7,7 +7,6 @@
//
//---------------------------------------------------------------------------
#include "shared/log.h"
#include "shared/rascsi_version.h"
#include "device.h"
#include <cassert>

View File

@ -9,6 +9,7 @@
#pragma once
#include "shared/log.h"
#include "generated/rascsi_interface.pb.h"
#include <unordered_map>
#include <string>
@ -72,6 +73,8 @@ class Device //NOSONAR The number of fields and methods is justified, the comple
protected:
Device(PbDeviceType, int);
void SetReady(bool b) { ready = b; }
bool IsReset() const { return reset; }
void SetReset(bool b) { reset = b; }
@ -89,8 +92,6 @@ protected:
string GetParam(const string&) const;
void SetParams(const unordered_map<string, string>&);
Device(PbDeviceType, int);
public:
virtual ~Device() = default;

View File

@ -0,0 +1,75 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "shared/log.h"
#include "device_logger.h"
using namespace std;
void DeviceLogger::Trace(const string& message) const
{
if (const string m = GetLogMessage(message); !m.empty()) {
LOGTRACE("%s", m.c_str())
}
}
void DeviceLogger::Debug(const string& message) const
{
if (const string m = GetLogMessage(message); !m.empty()) {
LOGDEBUG("%s", m.c_str())
}
}
void DeviceLogger::Info(const string& message) const
{
if (const string m = GetLogMessage(message); !m.empty()) {
LOGINFO("%s", m.c_str())
}
}
void DeviceLogger::Warn(const string& message) const
{
if (const string m = GetLogMessage(message); !m.empty()) {
LOGWARN("%s", m.c_str())
}
}
void DeviceLogger::Error(const string& message) const
{
if (const string m = GetLogMessage(message); !m.empty()) {
LOGERROR("%s", m.c_str())
}
}
string DeviceLogger::GetLogMessage(const string& message) const
{
if (log_device_id == -1 || (log_device_id == id && (log_device_lun == -1 || log_device_lun == lun)))
{
if (lun == -1) {
return "(ID " + to_string(id) + ") - " + message;
}
else {
return "(ID:LUN " + to_string(id) + ":" + to_string(lun) + ") - " + message;
}
}
return "";
}
void DeviceLogger::SetIdAndLun(int i, int l)
{
id = i;
lun = l;
}
void DeviceLogger::SetLogIdAndLun(int i, int l)
{
log_device_id = i;
log_device_lun = l;
}

View File

@ -0,0 +1,44 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#pragma once
#include <string>
using namespace std;
class DeviceLogger
{
public:
DeviceLogger() = default;
~DeviceLogger() = default;
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;
string GetLogMessage(const string&) const;
void SetIdAndLun(int, int);
static void SetLogIdAndLun(int, int);
private:
int id = -1;
int lun = -1;
// TODO Try to only have one shared instance, so that these fields do not have to be static
static inline int log_device_id = -1;
static inline int log_device_lun = -1;
};

View File

@ -14,10 +14,11 @@
//
//---------------------------------------------------------------------------
#include "shared/log.h"
#include "shared/rascsi_exceptions.h"
#include "scsi_command_util.h"
#include "disk.h"
#include <sstream>
#include <iomanip>
using namespace scsi_defs;
using namespace scsi_command_util;
@ -116,7 +117,7 @@ void Disk::Read(access_mode mode)
GetController()->SetBlocks(blocks);
GetController()->SetLength(Read(GetController()->GetCmd(), GetController()->GetBuffer(), start));
LOGTRACE("%s ctrl.length is %d", __PRETTY_FUNCTION__, GetController()->GetLength())
GetLogger().Trace("Length is " + to_string(GetController()->GetLength()));
// Set next block
GetController()->SetNext(start + 1);
@ -203,10 +204,10 @@ void Disk::StartStopUnit()
const bool load = GetController()->GetCmd(4) & 0x02;
if (load) {
LOGTRACE(start ? "Loading medium" : "Ejecting medium")
GetLogger().Trace(start ? "Loading medium" : "Ejecting medium");
}
else {
LOGTRACE(start ? "Starting unit" : "Stopping unit")
GetLogger().Trace(start ? "Starting unit" : "Stopping unit");
SetStopped(!start);
}
@ -238,7 +239,7 @@ void Disk::PreventAllowMediumRemoval()
const bool lock = GetController()->GetCmd(4) & 0x01;
LOGTRACE(lock ? "Locking medium" : "Unlocking medium")
GetLogger().Trace(lock ? "Locking medium" : "Unlocking medium");
SetLocked(lock);
@ -618,8 +619,8 @@ void Disk::ValidateBlockAddress(access_mode mode) const
const uint64_t block = mode == RW16 ? GetInt64(GetController()->GetCmd(), 2) : GetInt32(GetController()->GetCmd(), 2);
if (block > GetBlockCount()) {
LOGTRACE("%s", ("Capacity of " + to_string(GetBlockCount()) + " block(s) exceeded: Trying to access block "
+ to_string(block)).c_str())
GetLogger().Trace("Capacity of " + to_string(GetBlockCount()) + " block(s) exceeded: Trying to access block "
+ to_string(block));
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
}
}
@ -651,13 +652,14 @@ tuple<bool, uint64_t, uint32_t> Disk::CheckAndGetStartAndCount(access_mode mode)
}
}
LOGTRACE("%s READ/WRITE/VERIFY/SEEK command record=$%08X blocks=%d", __PRETTY_FUNCTION__,
static_cast<uint32_t>(start), count)
stringstream s;
s << "READ/WRITE/VERIFY/SEEK, start block: $" << setfill('0') << setw(8) << hex << start;
GetLogger().Trace(s.str() + ", blocks: " + to_string(count));
// Check capacity
if (uint64_t capacity = GetBlockCount(); !capacity || start > capacity || start + count > capacity) {
LOGTRACE("%s", ("Capacity of " + to_string(capacity) + " block(s) exceeded: Trying to access block "
+ to_string(start) + ", block count " + to_string(count)).c_str())
GetLogger().Trace("Capacity of " + to_string(capacity) + " block(s) exceeded: Trying to access block "
+ to_string(start) + ", block count " + to_string(count));
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
}

View File

@ -75,7 +75,7 @@ bool DiskTrack::Load(const string& path)
if (dt.buffer == nullptr) {
if (posix_memalign((void **)&dt.buffer, 512, ((length + 511) / 512) * 512)) {
LOGWARN("%s posix_memalign failed", __PRETTY_FUNCTION__)
LOGWARN("posix_memalign failed")
}
dt.length = length;
}
@ -88,7 +88,7 @@ bool DiskTrack::Load(const string& path)
if (dt.length != static_cast<uint32_t>(length)) {
free(dt.buffer);
if (posix_memalign((void **)&dt.buffer, 512, ((length + 511) / 512) * 512)) {
LOGWARN("%s posix_memalign failed", __PRETTY_FUNCTION__)
LOGWARN("posix_memalign failed")
}
dt.length = length;
}
@ -219,8 +219,6 @@ bool DiskTrack::ReadSector(vector<uint8_t>& buf, int sec) const
{
assert(sec >= 0 && sec < 0x100);
LOGTRACE("%s reading sector: %d", __PRETTY_FUNCTION__,sec)
// Error if not initialized
if (!dt.init) {
return false;

View File

@ -9,11 +9,12 @@
//
//---------------------------------------------------------------------------
#include "shared/log.h"
#include "shared/rascsi_exceptions.h"
#include "scsi_command_util.h"
#include "mode_page_device.h"
#include <cstddef>
#include <sstream>
#include <iomanip>
using namespace std;
using namespace scsi_defs;
@ -43,14 +44,17 @@ int ModePageDevice::AddModePages(const vector<int>& cdb, vector<uint8_t>& buf, i
// Get page code (0x3f means all pages)
const int page = cdb[2] & 0x3f;
LOGTRACE("%s Requesting mode page $%02X", __PRETTY_FUNCTION__, page)
stringstream s;
s << "Requesting mode page $" << setfill('0') << setw(2) << hex << page;
GetLogger().Trace(s.str());
// Mode page data mapped to the respective page numbers, C++ maps are ordered by key
map<int, vector<byte>> pages;
SetUpModePages(pages, page, changeable);
if (pages.empty()) {
LOGTRACE("%s Unsupported mode page $%02X", __PRETTY_FUNCTION__, page)
s << "Unsupported mode page $" << page;
GetLogger().Trace(s.str());
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}

View File

@ -7,10 +7,11 @@
//
//---------------------------------------------------------------------------
#include "shared/log.h"
#include "shared/rascsi_exceptions.h"
#include "scsi_command_util.h"
#include "primary_device.h"
#include <sstream>
#include <iomanip>
using namespace std;
using namespace scsi_defs;
@ -41,13 +42,17 @@ void PrimaryDevice::AddCommand(scsi_command opcode, const operation& execute)
void PrimaryDevice::Dispatch(scsi_command cmd)
{
stringstream s;
s << "$" << setfill('0') << setw(2) << hex << static_cast<int>(cmd);
if (const auto& it = commands.find(cmd); it != commands.end()) {
LOGDEBUG("Executing %s ($%02X)", command_mapping.find(cmd)->second.second, static_cast<int>(cmd))
GetLogger().Debug("Device is executing " + string(command_mapping.find(cmd)->second.second) +
" (" + s.str() + ")");
it->second();
}
else {
LOGTRACE("ID %d LUN %d received unsupported command: $%02X", GetId(), GetLun(), static_cast<int>(cmd))
GetLogger().Trace("Received unsupported command: " + s.str());
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
}
@ -63,7 +68,7 @@ void PrimaryDevice::Reset()
int PrimaryDevice::GetId() const
{
if (GetController() == nullptr) {
LOGERROR("Device is missing its controller")
GetLogger().Error("Device is missing its controller");
}
return GetController() != nullptr ? GetController()->GetTargetId() : -1;
@ -72,6 +77,8 @@ int PrimaryDevice::GetId() const
void PrimaryDevice::SetController(shared_ptr<AbstractController> c)
{
controller = c;
logger.SetIdAndLun(c != nullptr ? c->GetTargetId() : -1, GetLun());
}
void PrimaryDevice::TestUnitReady()
@ -97,7 +104,7 @@ void PrimaryDevice::Inquiry()
// Report if the device does not support the requested LUN
if (int lun = GetController()->GetEffectiveLun(); !GetController()->HasDeviceForLun(lun)) {
LOGTRACE("Reporting LUN %d for device ID %d as not supported", lun, GetId())
GetLogger().Trace("LUN is not available");
// Signal that the requested LUN does not exist
GetController()->GetBuffer().data()[0] = 0x7f;
@ -183,25 +190,24 @@ void PrimaryDevice::CheckReady()
// Not ready if reset
if (IsReset()) {
SetReset(false);
LOGTRACE("%s Device in reset", __PRETTY_FUNCTION__)
GetLogger().Trace("Device in reset");
throw scsi_exception(sense_key::UNIT_ATTENTION, asc::POWER_ON_OR_RESET);
}
// Not ready if it needs attention
if (IsAttn()) {
SetAttn(false);
LOGTRACE("%s Device in needs attention", __PRETTY_FUNCTION__)
GetLogger().Trace("Device in needs attention");
throw scsi_exception(sense_key::UNIT_ATTENTION, asc::NOT_READY_TO_READY_CHANGE);
}
// Return status if not ready
if (!IsReady()) {
LOGTRACE("%s Device not ready", __PRETTY_FUNCTION__)
GetLogger().Trace("Device not ready");
throw scsi_exception(sense_key::NOT_READY, asc::MEDIUM_NOT_PRESENT);
}
// Initialization with no error
LOGTRACE("%s Device is ready", __PRETTY_FUNCTION__)
GetLogger().Trace("Device is ready");
}
vector<uint8_t> PrimaryDevice::HandleInquiry(device_type type, scsi_level level, bool is_removable) const
@ -246,15 +252,19 @@ vector<byte> PrimaryDevice::HandleRequestSense() const
buf[12] = (byte)(GetStatusCode() >> 8);
buf[13] = (byte)GetStatusCode();
LOGTRACE("%s Status $%02X, Sense Key $%02X, ASC $%02X",__PRETTY_FUNCTION__, static_cast<int>(GetController()->GetStatus()),
static_cast<int>(buf[2]), static_cast<int>(buf[12]))
stringstream s;
s << setfill('0') << setw(2) << hex
<< "Status $" << static_cast<int>(GetController()->GetStatus())
<< ", Sense Key $" << static_cast<int>(buf[2])
<< ", ASC $" << static_cast<int>(buf[12]);
GetLogger().Trace(s.str());
return buf;
}
bool PrimaryDevice::WriteByteSequence(vector<uint8_t>&, uint32_t)
{
LOGERROR("%s Writing bytes is not supported by this device", __PRETTY_FUNCTION__)
GetLogger().Error("Writing bytes is not supported by this device");
return false;
}
@ -264,10 +274,10 @@ void PrimaryDevice::ReserveUnit()
reserving_initiator = GetController()->GetInitiatorId();
if (reserving_initiator != -1) {
LOGTRACE("Reserved device ID %d, LUN %d for initiator ID %d", GetId(), GetLun(), reserving_initiator)
GetLogger().Trace("Reserved device for initiator ID " + to_string(reserving_initiator));
}
else {
LOGTRACE("Reserved device ID %d, LUN %d for unknown initiator", GetId(), GetLun())
GetLogger().Trace("Reserved device for unknown initiator");
}
EnterStatusPhase();
@ -276,10 +286,10 @@ void PrimaryDevice::ReserveUnit()
void PrimaryDevice::ReleaseUnit()
{
if (reserving_initiator != -1) {
LOGTRACE("Released device ID %d, LUN %d reserved by initiator ID %d", GetId(), GetLun(), reserving_initiator)
GetLogger().Trace("Released device reserved by initiator ID " + to_string(reserving_initiator));
}
else {
LOGTRACE("Released device ID %d, LUN %d reserved by unknown initiator", GetId(), GetLun())
GetLogger().Trace("Released device reserved by unknown initiator");
}
DiscardReservation();
@ -303,10 +313,10 @@ bool PrimaryDevice::CheckReservation(int initiator_id, scsi_command cmd, bool pr
}
if (initiator_id != -1) {
LOGTRACE("Initiator ID %d tries to access reserved device ID %d, LUN %d", initiator_id, GetId(), GetLun())
GetLogger().Trace("Initiator ID " + to_string(initiator_id) + " tries to access reserved device");
}
else {
LOGTRACE("Unknown initiator tries to access reserved device ID %d, LUN %d", GetId(), GetLun())
GetLogger().Trace("Unknown initiator tries to access reserved device");
}
return false;

View File

@ -15,6 +15,7 @@
#include "interfaces/scsi_primary_commands.h"
#include "controllers/abstract_controller.h"
#include "device.h"
#include "device_logger.h"
#include <string>
#include <unordered_map>
#include <functional>
@ -56,6 +57,8 @@ protected:
void AddCommand(scsi_command, const operation&);
const DeviceLogger& GetLogger() const { return logger; }
vector<uint8_t> HandleInquiry(scsi_defs::device_type, scsi_level, bool) const;
virtual vector<uint8_t> InquiryInternal() const = 0;
void CheckReady();
@ -83,6 +86,8 @@ private:
vector<byte> HandleRequestSense() const;
DeviceLogger logger;
weak_ptr<AbstractController> controller;
unordered_map<scsi_command, operation> commands;

View File

@ -7,14 +7,18 @@
//
//---------------------------------------------------------------------------
#include "shared/log.h"
#include "shared/rascsi_exceptions.h"
#include "device_logger.h"
#include "scsi_command_util.h"
#include <cstring>
#include <cassert>
#include <sstream>
#include <iomanip>
using namespace scsi_defs;
void scsi_command_util::ModeSelect(scsi_command cmd, const vector<int>& cdb, const vector<uint8_t>& buf, int length,
int sector_size)
void scsi_command_util::ModeSelect(const DeviceLogger& logger, scsi_command cmd, const vector<int>& cdb,
const vector<uint8_t>& buf, int length, int sector_size)
{
assert(cmd == scsi_command::eCmdModeSelect6 || cmd == scsi_command::eCmdModeSelect10);
assert(length >= 0);
@ -52,14 +56,16 @@ void scsi_command_util::ModeSelect(scsi_command cmd, const vector<int>& cdb, con
if (GetInt16(buf, offset + 12) != sector_size) {
// With rascsi it is not possible to permanently (by formatting) change the sector size,
// because the size is an externally configurable setting only
LOGWARN("In order to change the sector size use the -b option when launching rascsi")
logger.Warn("In order to change the sector size use the -b option when launching rascsi");
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST);
}
has_valid_page_code = true;
}
else {
LOGWARN("Unknown MODE SELECT page code: $%02X", page)
stringstream s;
s << "Unknown MODE SELECT page code: $" << setfill('0') << setw(2) << hex << page;
logger.Warn(s.str());
}
// Advance to the next page

View File

@ -17,9 +17,11 @@
using namespace std;
class DeviceLogger;
namespace scsi_command_util
{
void ModeSelect(scsi_defs::scsi_command, const vector<int>&, const vector<uint8_t>&, int, int);
void ModeSelect(const DeviceLogger&, scsi_defs::scsi_command, const vector<int>&, const vector<uint8_t>&, int, int);
void EnrichFormatPage(map<int, vector<byte>>&, bool, int);
void AddAppleVendorModePage(map<int, vector<byte>>&, bool);

View File

@ -24,14 +24,20 @@
// Note: This requires a DaynaPort SCSI Link driver.
//---------------------------------------------------------------------------
#include "shared/log.h"
#include "shared/rascsi_exceptions.h"
#include "scsi_command_util.h"
#include "scsi_daynaport.h"
#include <sstream>
#include <iomanip>
using namespace scsi_defs;
using namespace scsi_command_util;
SCSIDaynaPort::SCSIDaynaPort(int lun) : PrimaryDevice(SCDP, lun)
{
SupportsParams(true);
}
bool SCSIDaynaPort::Init(const unordered_map<string, string>& params)
{
PrimaryDevice::Init(params);
@ -48,18 +54,16 @@ bool SCSIDaynaPort::Init(const unordered_map<string, string>& params)
// In the MacOS driver, it looks like the driver is doing two "READ" system calls.
SetSendDelay(DAYNAPORT_READ_HEADER_SZ);
SupportsParams(true);
m_bTapEnable = m_tap.Init(GetParams());
if(!m_bTapEnable){
LOGERROR("Unable to open the TAP interface")
GetLogger().Error("Unable to open the TAP interface");
// Not terminating on regular Linux PCs is helpful for testing
#if !defined(__x86_64__) && !defined(__X86__)
return false;
#endif
} else {
LOGDEBUG("Tap interface created")
GetLogger().Trace("Tap interface created");
}
Reset();
@ -118,7 +122,8 @@ int SCSIDaynaPort::Read(const vector<int>& cdb, vector<uint8_t>& buf, uint64_t)
const auto response = (scsi_resp_read_t*)buf.data();
const int requested_length = cdb[4];
LOGTRACE("%s Read maximum length %d, (%04X)", __PRETTY_FUNCTION__, requested_length, requested_length)
GetLogger().Trace("Read maximum length: " + to_string(requested_length));
// At startup the host may send a READ(6) command with a sector count of 1 to read the root sector.
// We should respond by going into the status mode with a code of 0x02.
@ -141,13 +146,13 @@ int SCSIDaynaPort::Read(const vector<int>& cdb, vector<uint8_t>& buf, uint64_t)
// If we didn't receive anything, return size of 0
if (rx_packet_size <= 0) {
LOGTRACE("%s No packet received", __PRETTY_FUNCTION__)
GetLogger().Trace("No packet received");
response->length = 0;
response->flags = read_data_flags_t::e_no_more_data;
return DAYNAPORT_READ_HEADER_SZ;
}
LOGTRACE("%s Packet Sz %d (%08X) read: %d", __PRETTY_FUNCTION__, (unsigned int) rx_packet_size, (unsigned int) rx_packet_size, read_count)
GetLogger().Trace("Packet Size " + to_string(rx_packet_size) + ", read count: " + to_string(read_count));
// This is a very basic filter to prevent unnecessary packets from
// being sent to the SCSI initiator.
@ -177,14 +182,12 @@ int SCSIDaynaPort::Read(const vector<int>& cdb, vector<uint8_t>& buf, uint64_t)
// configuration from SCSI command 0x0D
if (!send_message_to_host) {
LOGDEBUG("%s Received a packet that's not for me: %02X %02X %02X %02X %02X %02X", \
__PRETTY_FUNCTION__,
static_cast<int>(response->data[0]),
static_cast<int>(response->data[1]),
static_cast<int>(response->data[2]),
static_cast<int>(response->data[3]),
static_cast<int>(response->data[4]),
static_cast<int>(response->data[5]))
stringstream s;
s << "Received a packet that's not for me:" << setfill('0') << setw(2) << hex;
for (int i = 0 ; i < 6; i++) {
s << " $" << static_cast<int>(response->data[i]);
}
GetLogger().Debug(s.str());
// If there are pending packets to be processed, we'll tell the host that the read
// length was 0.
@ -253,16 +256,18 @@ bool SCSIDaynaPort::WriteBytes(const vector<int>& cdb, vector<uint8_t>& buf, uin
if (data_format == 0x00) {
m_tap.Send(buf.data(), data_length);
LOGTRACE("%s Transmitted %u bytes (00 format)", __PRETTY_FUNCTION__, data_length)
GetLogger().Trace("Transmitted " + to_string(data_length) + " byte(s) (00 format)");
}
else if (data_format == 0x80) {
// The data length is specified in the first 2 bytes of the payload
data_length = buf[1] + ((static_cast<int>(buf[0]) & 0xff) << 8);
m_tap.Send(&(buf.data()[4]), data_length);
LOGTRACE("%s Transmitted %u bytes (80 format)", __PRETTY_FUNCTION__, data_length)
GetLogger().Trace("Transmitted " + to_string(data_length) + "byte(s) (80 format)");
}
else {
LOGWARN("%s Unknown data format %02X", __PRETTY_FUNCTION__, data_format)
stringstream s;
s << "Unknown data format: " << setfill('0') << setw(2) << hex << data_format;
GetLogger().Warn(s.str());
}
GetController()->SetBlocks(0);
@ -308,15 +313,16 @@ void SCSIDaynaPort::Read6()
// If any commands have a bogus control value, they were probably not
// generated by the DaynaPort driver so ignore them
if (GetController()->GetCmd(5) != 0xc0 && GetController()->GetCmd(5) != 0x80) {
LOGTRACE("%s Control value %d, (%04X), returning invalid CDB", __PRETTY_FUNCTION__,
GetController()->GetCmd(5), GetController()->GetCmd(5))
GetLogger().Trace("Control value: " + to_string(GetController()->GetCmd(5)));
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
LOGTRACE("%s READ(6) command record=%d blocks=%d", __PRETTY_FUNCTION__, record, GetController()->GetBlocks())
stringstream s;
s << "READ(6) command, record: $" << setfill('0') << setw(8) << hex << record;
GetLogger().Trace(s.str() + ", blocks: " + to_string(GetController()->GetBlocks()));
GetController()->SetLength(Read(GetController()->GetCmd(), GetController()->GetBuffer(), record));
LOGTRACE("%s ctrl.length is %d", __PRETTY_FUNCTION__, GetController()->GetLength())
GetLogger().Trace("Length is " + to_string(GetController()->GetLength()));
// Set next block
GetController()->SetNext(record + 1);
@ -338,10 +344,14 @@ void SCSIDaynaPort::Write6() const
GetController()->SetLength(GetInt16(GetController()->GetCmd(), 3) + 8);
}
else {
LOGWARN("%s Unknown data format $%02X", __PRETTY_FUNCTION__, data_format)
stringstream s;
s << "Unknown data format: " << setfill('0') << setw(2) << hex << data_format;
GetLogger().Warn(s.str());
}
LOGTRACE("%s length: $%04X (%d) format: $%02X", __PRETTY_FUNCTION__, GetController()->GetLength(),
GetController()->GetLength(), data_format)
stringstream s;
s << "Length: " << GetController()->GetLength() << ", format: $" << setfill('0') << setw(2) << hex << data_format;
GetLogger().Trace(s.str());
if (GetController()->GetLength() <= 0) {
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
@ -398,7 +408,7 @@ void SCSIDaynaPort::SetInterfaceMode() const
GetController()->SetLength(RetrieveStats(GetController()->GetCmd(), GetController()->GetBuffer()));
switch(GetController()->GetCmd(5)){
case CMD_SCSILINK_SETMODE:
// TODO Not implemented, do nothing
// Not implemented, do nothing
EnterStatusPhase();
break;
@ -407,15 +417,10 @@ void SCSIDaynaPort::SetInterfaceMode() const
EnterDataOutPhase();
break;
case CMD_SCSILINK_STATS:
case CMD_SCSILINK_ENABLE:
case CMD_SCSILINK_SET:
LOGWARN("%s Unsupported SetInterface command received: %02X", __PRETTY_FUNCTION__, GetController()->GetCmd(5))
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
break;
default:
LOGWARN("%s Unknown SetInterface command received: %02X", __PRETTY_FUNCTION__, GetController()->GetCmd(5))
stringstream s;
s << "Unsupported SetInterface command: " << setfill('0') << setw(2) << hex << GetController()->GetCmd(5);
GetLogger().Warn(s.str());
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
break;
}
@ -425,8 +430,9 @@ void SCSIDaynaPort::SetMcastAddr() const
{
GetController()->SetLength(GetController()->GetCmd(4));
if (GetController()->GetLength() == 0) {
LOGWARN("%s Not supported SetMcastAddr Command %02X", __PRETTY_FUNCTION__, GetController()->GetCmd(2))
stringstream s;
s << "Unsupported SetMcastAddr command: " << setfill('0') << setw(2) << hex << GetController()->GetCmd(2);
GetLogger().Warn(s.str());
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
@ -449,23 +455,23 @@ void SCSIDaynaPort::EnableInterface()
{
if (GetController()->GetCmd(5) & 0x80) {
if (!m_tap.Enable()) {
LOGWARN("Unable to enable the DaynaPort Interface")
GetLogger().Warn("Unable to enable the DaynaPort Interface");
throw scsi_exception(sense_key::ABORTED_COMMAND);
}
m_tap.Flush();
LOGINFO("The DaynaPort interface has been ENABLED")
GetLogger().Info("The DaynaPort interface has been ENABLED");
}
else {
if (!m_tap.Disable()) {
LOGWARN("Unable to disable the DaynaPort Interface")
GetLogger().Warn("Unable to disable the DaynaPort Interface");
throw scsi_exception(sense_key::ABORTED_COMMAND);
}
LOGINFO("The DaynaPort interface has been DISABLED")
GetLogger().Info("The DaynaPort interface has been DISABLED");
}
EnterStatusPhase();

View File

@ -45,7 +45,7 @@ class SCSIDaynaPort : public PrimaryDevice, public ByteWriter
{
public:
explicit SCSIDaynaPort(int lun) : PrimaryDevice(SCDP, lun) {}
explicit SCSIDaynaPort(int);
~SCSIDaynaPort() override = default;
bool Init(const unordered_map<string, string>&) override;

View File

@ -16,7 +16,6 @@
// work with the Sharp X68000 operating system.
//---------------------------------------------------------------------------
#include "shared/log.h"
#include "shared/rascsi_exceptions.h"
#include "scsi_command_util.h"
#include "scsi_host_bridge.h"
@ -27,6 +26,11 @@ using namespace std;
using namespace scsi_defs;
using namespace scsi_command_util;
SCSIBR::SCSIBR(int lun) : PrimaryDevice(SCBR, lun)
{
SupportsParams(true);
}
bool SCSIBR::Init(const unordered_map<string, string>& params)
{
PrimaryDevice::Init(params);
@ -38,13 +42,11 @@ bool SCSIBR::Init(const unordered_map<string, string>& params)
AddCommand(scsi_command::eCmdGetMessage10, [this] { GetMessage10(); });
AddCommand(scsi_command::eCmdSendMessage10, [this] { SendMessage10(); });
SupportsParams(true);
#ifdef __linux__
// TAP Driver Generation
m_bTapEnable = tap.Init(GetParams());
if (!m_bTapEnable){
LOGERROR("Unable to open the TAP interface")
GetLogger().Error("Unable to open the TAP interface");
return false;
}
#endif

View File

@ -32,7 +32,7 @@ class SCSIBR : public PrimaryDevice, public ByteWriter
public:
explicit SCSIBR(int lun) : PrimaryDevice(SCBR, lun) {}
explicit SCSIBR(int);
~SCSIBR() override = default;
bool Init(const unordered_map<string, string>&) override;

View File

@ -29,7 +29,6 @@
// With STOP PRINT printing can be cancelled before SYNCHRONIZE BUFFER was sent.
//
#include "shared/log.h"
#include "shared/rascsi_exceptions.h"
#include "scsi_command_util.h"
#include "scsi_printer.h"
@ -41,6 +40,11 @@ using namespace filesystem;
using namespace scsi_defs;
using namespace scsi_command_util;
SCSIPrinter::SCSIPrinter(int lun) : PrimaryDevice(SCLP, lun)
{
SupportsParams(true);
}
bool SCSIPrinter::Init(const unordered_map<string, string>& params)
{
PrimaryDevice::Init(params);
@ -57,7 +61,7 @@ bool SCSIPrinter::Init(const unordered_map<string, string>& params)
AddCommand(scsi_command::eCmdSendDiagnostic, [this] { SendDiagnostic(); });
if (GetParam("cmd").find("%f") == string::npos) {
LOGERROR("Missing filename specifier %%f")
GetLogger().Trace("Missing filename specifier %f");
return false;
}
@ -65,7 +69,6 @@ bool SCSIPrinter::Init(const unordered_map<string, string>& params)
file_template = temp_directory_path(error); //NOSONAR Publicly writable directory is fine here
file_template += PRINTER_FILE_PATTERN;
SupportsParams(true);
SetReady(true);
return true;
@ -86,11 +89,11 @@ void SCSIPrinter::Print()
{
const uint32_t length = GetInt24(GetController()->GetCmd(), 2);
LOGTRACE("Receiving %d byte(s) to be printed", length)
GetLogger().Trace("Receiving " + to_string(length) + " byte(s) to be printed");
if (length > GetController()->GetBuffer().size()) {
LOGERROR("%s", ("Transfer buffer overflow: Buffer size is " + to_string(GetController()->GetBuffer().size()) +
" bytes, " + to_string(length) + " bytes expected").c_str())
GetLogger().Error("Transfer buffer overflow: Buffer size is " + to_string(GetController()->GetBuffer().size()) +
" bytes, " + to_string(length) + " bytes expected");
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
}
@ -104,7 +107,7 @@ void SCSIPrinter::Print()
void SCSIPrinter::SynchronizeBuffer()
{
if (!out.is_open()) {
LOGWARN("Nothing to print")
GetLogger().Warn("Nothing to print");
throw scsi_exception(sense_key::ABORTED_COMMAND);
}
@ -116,12 +119,12 @@ void SCSIPrinter::SynchronizeBuffer()
error_code error;
LOGTRACE("Printing file '%s' with %s byte(s)", filename.c_str(), to_string(file_size(path(filename), error)).c_str())
GetLogger().Trace("Printing file '" + filename + "' with " + to_string(file_size(path(filename), error)) + " byte(s)");
LOGDEBUG("Executing '%s'", cmd.c_str())
GetLogger().Debug("Executing '" + cmd + "'");
if (system(cmd.c_str())) {
LOGERROR("Printing file '%s' failed, the printing system might not be configured", filename.c_str())
GetLogger().Error("Printing file '" + filename + "' failed, the printing system might not be configured");
Cleanup();
@ -142,7 +145,7 @@ bool SCSIPrinter::WriteByteSequence(vector<uint8_t>& buf, uint32_t length)
// There is no C++ API that generates a file with a unique name
const int fd = mkstemp(f.data());
if (fd == -1) {
LOGERROR("Can't create printer output file for pattern '%s': %s", filename.c_str(), strerror(errno))
GetLogger().Error("Can't create printer output file for pattern '" + filename + "': " + strerror(errno));
return false;
}
close(fd);
@ -154,10 +157,10 @@ bool SCSIPrinter::WriteByteSequence(vector<uint8_t>& buf, uint32_t length)
throw scsi_exception(sense_key::ABORTED_COMMAND);
}
LOGTRACE("Created printer output file '%s'", filename.c_str())
GetLogger().Trace("Created printer output file '" + filename + "'");
}
LOGTRACE("Appending %d byte(s) to printer output file '%s'", length, filename.c_str())
GetLogger().Trace("Appending " + to_string(length) + " byte(s) to printer output file ''" + filename + "'");
out.write((const char*)buf.data(), length);

View File

@ -26,7 +26,7 @@ class SCSIPrinter : public PrimaryDevice, private ScsiPrinterCommands
public:
explicit SCSIPrinter(int lun) : PrimaryDevice(SCLP, lun) {}
explicit SCSIPrinter(int);
~SCSIPrinter() override = default;
bool Init(const unordered_map<string, string>&) override;

View File

@ -26,6 +26,10 @@ using namespace scsi_command_util;
SCSICD::SCSICD(int lun, const unordered_set<uint32_t>& sector_sizes) : Disk(SCCD, lun)
{
SetSectorSizes(sector_sizes);
SetReadOnly(true);
SetRemovable(true);
SetLockable(true);
}
bool SCSICD::Init(const unordered_map<string, string>& params)
@ -34,10 +38,6 @@ bool SCSICD::Init(const unordered_map<string, string>& params)
AddCommand(scsi_command::eCmdReadToc, [this] { ReadToc(); });
SetReadOnly(true);
SetRemovable(true);
SetLockable(true);
return true;
}

View File

@ -86,7 +86,7 @@ vector<uint8_t> SCSIHD::InquiryInternal() const
void SCSIHD::ModeSelect(scsi_command cmd, const vector<int>& cdb, const vector<uint8_t>& buf, int length) const
{
scsi_command_util::ModeSelect(cmd, cdb, buf, length, 1 << GetSectorSizeShiftCount());
scsi_command_util::ModeSelect(GetLogger(), cmd, cdb, buf, length, 1 << GetSectorSizeShiftCount());
}
void SCSIHD::AddFormatPage(map<int, vector<byte>>& pages, bool changeable) const

View File

@ -91,7 +91,7 @@ void SCSIMO::AddOptionPage(map<int, vector<byte>>& pages, bool) const
void SCSIMO::ModeSelect(scsi_command cmd, const vector<int>& cdb, const vector<uint8_t>& buf, int length) const
{
scsi_command_util::ModeSelect(cmd, cdb, buf, length, 1 << GetSectorSizeShiftCount());
scsi_command_util::ModeSelect(GetLogger(), cmd, cdb, buf, length, 1 << GetSectorSizeShiftCount());
}
//

View File

@ -26,9 +26,6 @@
using namespace std;
using namespace ras_util;
// TODO Should not be global and not be used by sm_vcd_report
double ns_per_loop;
void ScsiMon::KillHandler(int)
{
running = false;
@ -75,19 +72,6 @@ void ScsiMon::ParseArguments(const vector<char *>& args)
html_file_name += ".html";
}
void ScsiMon::PrintCopyrightText() const
{
LOGINFO("SCSI Monitor Capture Tool - part of RaSCSI(*^..^*) ")
LOGINFO("version %s (%s, %s)",
rascsi_get_version_string().c_str(),
__DATE__,
__TIME__)
LOGINFO("Powered by XM6 TypeG Technology ")
LOGINFO("Copyright (C) 2016-2020 GIMONS")
LOGINFO("Copyright (C) 2020-2022 Contributors to the RaSCSI project")
LOGINFO(" ")
}
void ScsiMon::PrintHelpText(const vector<char *>& args) const
{
LOGINFO("%s -i [input file json] -b [buffer size] [output file]", args[0])
@ -174,7 +158,8 @@ int ScsiMon::run(const vector<char *>& args)
#endif
spdlog::set_pattern("%^[%l]%$ %v");
PrintCopyrightText();
ras_util::Banner("SCSI Monitor Capture Tool");
ParseArguments(args);
#ifdef DEBUG

View File

@ -26,10 +26,11 @@ public:
int run(const vector<char *>&);
inline static double ns_per_loop;
private:
void ParseArguments(const vector<char *>&);
void PrintCopyrightText() const;
void PrintHelpText(const vector<char *>&) const;
void Banner() const;
bool Init();
@ -57,5 +58,4 @@ private:
string json_file_name;
string html_file_name;
string input_file_name;
};

View File

@ -12,6 +12,7 @@
#include "shared/log.h"
#include "hal/gpiobus.h"
#include "data_sample.h"
#include "sm_core.h"
#include "sm_reports.h"
#include <sstream>
#include <iostream>
@ -50,8 +51,6 @@ const int PIN_PHASE = 0;
//---------------------------------------------------------------------------
static uint8_t prev_value[32] = {0xFF};
extern double ns_per_loop;
static uint8_t get_pin_value(uint32_t data, int pin)
{
return (data >> pin) & 1;
@ -164,7 +163,7 @@ void scsimon_generate_value_change_dump(const char *filename, const data_capture
uint32_t i = 0;
while (i < capture_count) {
vcd_ofstream << "#" << (uint64_t)((double)data_capture_array[i].timestamp * ns_per_loop) << endl;
vcd_ofstream << "#" << (uint64_t)((double)data_capture_array[i].timestamp * ScsiMon::ns_per_loop) << endl;
vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_BSY, SYMBOL_PIN_BSY);
vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_SEL, SYMBOL_PIN_SEL);
vcd_output_if_changed_bool(vcd_ofstream, data_capture_array[i].data, PIN_CD, SYMBOL_PIN_CD);

View File

@ -133,6 +133,18 @@ void Rascsi::LogDevices(string_view devices) const
}
}
PbDeviceType Rascsi::ParseDeviceType(const string& value) const
{
string t = value;
PbDeviceType type;
transform(t.begin(), t.end(), t.begin(), ::toupper);
if (!PbDeviceType_Parse(t, &type)) {
throw parser_exception("Illegal device type '" + value + "'");
}
return type;
}
void Rascsi::TerminationHandler(int signum)
{
Cleanup();
@ -216,12 +228,11 @@ Rascsi::optargs_type Rascsi::ParseArguments(const vector<char *>& args, int& por
void Rascsi::CreateInitialDevices(const optargs_type& optargs) const
{
PbCommand command;
int id = -1;
int lun = -1;
PbDeviceType type = UNDEFINED;
int block_size = 0;
string name;
string log_level;
string id_and_lun;
const char *locale = setlocale(LC_MESSAGES, "");
if (locale == nullptr || !strcmp(locale, "C")) {
@ -233,15 +244,12 @@ void Rascsi::CreateInitialDevices(const optargs_type& optargs) const
switch (option) {
case 'i':
case 'I':
id = -1;
lun = -1;
continue;
case 'd':
case 'D': {
ProcessId(value, ScsiController::LUN_MAX, id, lun);
case 'D':
id_and_lun = value;
continue;
}
case 'z':
locale = value.c_str();
@ -271,13 +279,8 @@ void Rascsi::CreateInitialDevices(const optargs_type& optargs) const
}
continue;
case 't': {
string t = value;
transform(t.begin(), t.end(), t.begin(), ::toupper);
if (!PbDeviceType_Parse(t, &type)) {
throw parser_exception("Illegal device type '" + value + "'");
}
}
case 't':
type = ParseDeviceType(value);
continue;
case 1:
@ -288,35 +291,25 @@ void Rascsi::CreateInitialDevices(const optargs_type& optargs) const
throw parser_exception("Parser error");
}
// Set up the device data
PbDeviceDefinition *device = command.add_devices();
device->set_id(id);
device->set_unit(lun);
if (!id_and_lun.empty()) {
if (const string error = SetIdAndLun(*device, id_and_lun, ScsiController::LUN_MAX); !error.empty()) {
throw parser_exception(error);
}
}
device->set_type(type);
device->set_block_size(block_size);
ParseParameters(*device, value);
if (size_t separator_pos = name.find(COMPONENT_SEPARATOR); separator_pos != string::npos) {
device->set_vendor(name.substr(0, separator_pos));
name = name.substr(separator_pos + 1);
separator_pos = name.find(COMPONENT_SEPARATOR);
if (separator_pos != string::npos) {
device->set_product(name.substr(0, separator_pos));
device->set_revision(name.substr(separator_pos + 1));
}
else {
device->set_product(name);
}
}
else {
device->set_vendor(name);
}
SetProductData(*device, name);
id = -1;
type = UNDEFINED;
block_size = 0;
name = "";
id_and_lun = "";
}
// Attach all specified devices
@ -485,7 +478,7 @@ bool Rascsi::ExecuteCommand(const CommandContext& context, const PbCommand& comm
return true;
}
int Rascsi::run(const vector<char *>& args) const
int Rascsi::run(const vector<char *>& args)
{
GOOGLE_PROTOBUF_VERIFY_VERSION;
@ -579,22 +572,14 @@ int Rascsi::run(const vector<char *>& args) const
// Wait until BSY is released as there is a possibility for the
// initiator to assert it while setting the ID (for up to 3 seconds)
if (bus->GetBSY()) {
const uint32_t now = SysTimer::GetTimerLow();
while ((SysTimer::GetTimerLow() - now) < 3'000'000) {
bus->Acquire();
if (!bus->GetBSY()) {
break;
}
}
}
WaitForNotBusy();
// Stop because the bus is busy or another device responded
if (bus->GetBSY() || !bus->GetSEL()) {
continue;
}
int initiator_id = -1;
int initiator_id = AbstractController::UNKNOWN_INITIATOR_ID;
// The initiator and target ID
const uint8_t id_data = bus->GetDAT();
@ -604,8 +589,17 @@ int Rascsi::run(const vector<char *>& args) const
// Identify the responsible controller
auto controller = controller_manager->IdentifyController(id_data);
if (controller != nullptr) {
device_logger.SetIdAndLun(controller->GetTargetId(), -1);
initiator_id = controller->ExtractInitiatorId(id_data);
if (initiator_id != AbstractController::UNKNOWN_INITIATOR_ID) {
device_logger.Trace("++++ Starting processing for initiator ID " + to_string(initiator_id));
}
else {
device_logger.Trace("++++ Starting processing for unknown initiator ID");
}
if (controller->Process(initiator_id) == BUS::phase_t::selection) {
phase = BUS::phase_t::selection;
}
@ -648,3 +642,19 @@ int Rascsi::run(const vector<char *>& args) const
return EXIT_SUCCESS;
}
void Rascsi::WaitForNotBusy() const
{
if (bus->GetBSY()) {
const uint32_t now = SysTimer::GetTimerLow();
// Wait for 3s
while ((SysTimer::GetTimerLow() - now) < 3'000'000) {
bus->Acquire();
if (!bus->GetBSY()) {
break;
}
}
}
}

View File

@ -9,6 +9,7 @@
#pragma once
#include "devices/device_logger.h"
#include "rascsi/command_context.h"
#include "rascsi/rascsi_service.h"
#include "rascsi/rascsi_image.h"
@ -34,7 +35,7 @@ public:
Rascsi() = default;
~Rascsi() = default;
int run(const vector<char *>&) const;
int run(const vector<char *>&);
private:
@ -43,13 +44,17 @@ private:
static void Cleanup();
void ReadAccessToken(const string&) const;
void LogDevices(string_view) const;
PbDeviceType ParseDeviceType(const string&) const;
static void TerminationHandler(int);
optargs_type ParseArguments(const vector<char *>&, int&) const;
void CreateInitialDevices(const optargs_type&) const;
void WaitForNotBusy() const;
// TODO Should not be static and should be moved to RascsiService
static bool ExecuteCommand(const CommandContext&, const PbCommand&);
DeviceLogger device_logger;
// A static instance is needed because of the signal handler
static inline shared_ptr<BUS> bus;

View File

@ -13,6 +13,7 @@
#include "shared/rascsi_exceptions.h"
#include "controllers/controller_manager.h"
#include "controllers/scsi_controller.h"
#include "devices/device_logger.h"
#include "devices/device_factory.h"
#include "devices/primary_device.h"
#include "devices/disk.h"
@ -167,31 +168,49 @@ bool RascsiExecutor::ProcessCmd(const CommandContext& context, const PbCommand&
bool RascsiExecutor::SetLogLevel(const string& log_level) const
{
if (log_level == "trace") {
set_level(level::trace);
int id = -1;
int lun = -1;
string level = log_level;
if (size_t separator_pos = log_level.find(COMPONENT_SEPARATOR); separator_pos != string::npos) {
level = log_level.substr(0, separator_pos);
const string l = log_level.substr(separator_pos + 1);
separator_pos = l.find(COMPONENT_SEPARATOR);
if (separator_pos != string::npos) {
const string error = ProcessId(l, ScsiController::LUN_MAX, id, lun);
if (!error.empty()) {
LOGWARN("Invalid device ID/LUN specifier '%s'", l.c_str())
return false;
}
else if (log_level == "debug") {
set_level(level::debug);
}
else if (log_level == "info") {
set_level(level::info);
else if (!GetAsUnsignedInt(l, id)) {
LOGWARN("Invalid device ID specifier '%s'", l.c_str())
return false;
}
else if (log_level == "warn") {
set_level(level::warn);
}
else if (log_level == "err") {
set_level(level::err);
}
else if (log_level == "off") {
set_level(level::off);
if (const auto& it = log_level_mapping.find(level); it != log_level_mapping.end()) {
set_level(it->second);
}
else {
LOGWARN("Invalid log level '%s'", log_level.c_str())
return false;
}
LOGINFO("Set log level to '%s'", log_level.c_str())
DeviceLogger::SetLogIdAndLun(id, lun);
if (id != -1) {
if (lun == -1) {
LOGINFO("Set log level for device ID %d to '%s'", id, level.c_str())
}
else {
LOGINFO("Set log level for device ID %d, LUN %d to '%s'", id, lun, level.c_str())
}
}
else {
LOGINFO("Set log level to '%s'", level.c_str())
}
return true;
}

View File

@ -9,8 +9,11 @@
#pragma once
#include "spdlog/spdlog.h"
#include "shared/protobuf_serializer.h"
#include "rascsi/rascsi_response.h"
#include <unordered_set>
#include <unordered_map>
class RascsiImage;
class DeviceFactory;
@ -18,6 +21,8 @@ class ControllerManager;
class PrimaryDevice;
class CommandContext;
using namespace spdlog;
class RascsiExecutor
{
public:
@ -66,4 +71,13 @@ private:
const ProtobufSerializer serializer;
unordered_set<int> reserved_ids;
static inline const unordered_map<string, level::level_enum> log_level_mapping = {
{ "trace", level::trace },
{ "debug", level::debug },
{ "info", level::info },
{ "warn", level::warn },
{ "err", level::err },
{ "off", level::off }
};
};

View File

@ -5,8 +5,7 @@
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// Copyright (C) 2020-2021 Contributors to the RaSCSI project
// [ Send Control Command ]
// Copyright (C) 2020-2022 Contributors to the RaSCSI project
//
//---------------------------------------------------------------------------
@ -88,20 +87,12 @@ int RasCtl::run(const vector<char *>& args) const
while ((opt = getopt(static_cast<int>(args.size()), args.data(),
"e::lmos::vDINOTVXa:b:c:d:f:h:i:n:p:r:t:x:z:C:E:F:L:P::R:")) != -1) {
switch (opt) {
case 'i': {
int id;
int lun;
try {
ProcessId(optarg, ScsiController::LUN_MAX, id, lun);
}
catch(const parser_exception& e) {
cerr << "Error: " << e.what() << endl;
case 'i':
if (const string error = SetIdAndLun(*device, optarg, ScsiController::LUN_MAX); !error.empty()) {
cerr << "Error: " << error << endl;
exit(EXIT_FAILURE);
}
device->set_id(id);
device->set_unit(lun);
break;
}
case 'C':
command.set_operation(CREATE_IMAGE);
@ -206,32 +197,8 @@ int RasCtl::run(const vector<char *>& args) const
image_params = optarg;
break;
case 'n': {
string vendor;
string product;
string revision;
string s = optarg;
if (size_t separator_pos = s.find(COMPONENT_SEPARATOR); separator_pos != string::npos) {
vendor = s.substr(0, separator_pos);
s = s.substr(separator_pos + 1);
separator_pos = s.find(COMPONENT_SEPARATOR);
if (separator_pos != string::npos) {
product = s.substr(0, separator_pos);
revision = s.substr(separator_pos + 1);
}
else {
product = s;
}
}
else {
vendor = s;
}
device->set_vendor(vendor);
device->set_product(product);
device->set_revision(revision);
}
case 'n':
SetProductData(*device, optarg);
break;
case 'p':

View File

@ -9,9 +9,12 @@
#pragma once
#include "generated/rascsi_interface.pb.h"
#include <vector>
#include <string>
using namespace std;
using namespace rascsi_interface;
class RasCtl
{

View File

@ -107,8 +107,12 @@ void RasDump::ParseArguments(const vector<char *>& args)
break;
case 't':
ProcessId(optarg, 8, target_id, target_lun);
case 't': {
const string error = ProcessId(optarg, 8, target_id, target_lun);
if (!error.empty()) {
throw parser_exception(error);
}
}
break;
case 'v':

View File

@ -14,7 +14,6 @@
#include <string>
#include <vector>
#include <memory>
#include <stdexcept>
using namespace std;

View File

@ -6,14 +6,12 @@
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// Copyright (C) 2020 akuker
// [ Logging utilities ]
//
//---------------------------------------------------------------------------
#pragma once
#include "spdlog/spdlog.h"
#include "spdlog/sinks/sink.h"
static const int LOGBUF_SIZE = 512;

View File

@ -96,6 +96,41 @@ void protobuf_util::SetPatternParams(PbCommand& command, string_view patterns)
SetParam(command, "file_pattern", file_pattern);
}
void protobuf_util::SetProductData(PbDeviceDefinition& device, const string& data)
{
string name = data;
if (size_t separator_pos = name.find(COMPONENT_SEPARATOR); separator_pos != string::npos) {
device.set_vendor(name.substr(0, separator_pos));
name = name.substr(separator_pos + 1);
separator_pos = name.find(COMPONENT_SEPARATOR);
if (separator_pos != string::npos) {
device.set_product(name.substr(0, separator_pos));
device.set_revision(name.substr(separator_pos + 1));
}
else {
device.set_product(name);
}
}
else {
device.set_vendor(name);
}
}
string protobuf_util::SetIdAndLun(PbDeviceDefinition& device, const string& value, int max_luns)
{
int id;
int lun;
if (const string error = ProcessId(value, max_luns, id, lun); !error.empty()) {
return error;
}
device.set_id(id);
device.set_unit(lun);
return "";
}
string protobuf_util::ListDevices(const list<PbDevice>& pb_devices)
{
if (pb_devices.empty()) {

View File

@ -30,5 +30,7 @@ namespace protobuf_util
void SetParam(PbDevice&, const string&, string_view);
void SetParam(PbDeviceDefinition&, const string&, string_view);
void SetPatternParams(PbCommand&, string_view);
void SetProductData(PbDeviceDefinition&, const string&);
string SetIdAndLun(PbDeviceDefinition&, const string&, int);
string ListDevices(const list<PbDevice>&);
}

View File

@ -7,9 +7,9 @@
//
//---------------------------------------------------------------------------
#include "rascsi_exceptions.h"
#include "rascsi_version.h"
#include "rasutil.h"
#include <cassert>
#include <sstream>
#include <algorithm>
@ -35,19 +35,35 @@ bool ras_util::GetAsUnsignedInt(const string& value, int& result)
return true;
}
void ras_util::ProcessId(const string& id_spec, int max_luns, int& id, int& lun)
string ras_util::ProcessId(const string& id_spec, int max_luns, int& id, int& lun)
{
assert(max_luns > 0);
id = -1;
lun = -1;
if (id_spec.empty()) {
return "Missing device ID";
}
if (const size_t separator_pos = id_spec.find(COMPONENT_SEPARATOR); separator_pos == string::npos) {
if (!GetAsUnsignedInt(id_spec, id) || id >= 8) {
throw parser_exception("Invalid device ID (0-7)");
id = -1;
return "Invalid device ID (0-7)";
}
lun = 0;
}
else if (!GetAsUnsignedInt(id_spec.substr(0, separator_pos), id) || id > 7 ||
!GetAsUnsignedInt(id_spec.substr(separator_pos + 1), lun) || lun >= max_luns) {
throw parser_exception("Invalid LUN (0-" + to_string(max_luns - 1) + ")");
id = -1;
lun = -1;
return "Invalid LUN (0-" + to_string(max_luns - 1) + ")";
}
return "";
}
string ras_util::Banner(const string& app)

View File

@ -19,7 +19,7 @@ namespace ras_util
static const char COMPONENT_SEPARATOR = ':';
bool GetAsUnsignedInt(const string&, int&);
void ProcessId(const string&, int, int&, int&);
string ProcessId(const string&, int, int&, int&);
string Banner(const string&);
string GetExtensionLowerCase(const string&);

View File

@ -199,14 +199,13 @@ TEST(AbstractControllerTest, ExtractInitiatorId)
{
const int ID = 1;
const int INITIATOR_ID = 7;
const int UNKNOWN_INITIATOR_ID = -1;
auto bus = make_shared<MockBus>();
auto controller_manager = make_shared<ControllerManager>(*bus);
MockAbstractController controller(controller_manager, ID);
EXPECT_EQ(INITIATOR_ID, controller.ExtractInitiatorId((1 << INITIATOR_ID) | ( 1 << ID)));
EXPECT_EQ(UNKNOWN_INITIATOR_ID, controller.ExtractInitiatorId(1 << ID));
EXPECT_EQ(AbstractController::UNKNOWN_INITIATOR_ID, controller.ExtractInitiatorId(1 << ID));
}
TEST(AbstractControllerTest, GetOpcode)

View File

@ -140,9 +140,6 @@ TEST(DeviceFactoryTest, SCHD_Device_Defaults)
auto device = device_factory.CreateDevice(UNDEFINED, 0, "test.hda");
const unordered_map<string, string> params;
device->Init(params);
EXPECT_NE(nullptr, device);
EXPECT_EQ(SCHD, device->GetType());
EXPECT_TRUE(device->SupportsFile());
@ -179,8 +176,6 @@ void TestRemovableDrive(PbDeviceType type, const string& filename, const string&
{
DeviceFactory device_factory;
auto device = device_factory.CreateDevice(UNDEFINED, 0, filename);
const unordered_map<string, string> params;
device->Init(params);
EXPECT_NE(nullptr, device);
EXPECT_EQ(type, device->GetType());
@ -218,10 +213,6 @@ TEST(DeviceFactoryTest, SCCD_Device_Defaults)
DeviceFactory device_factory;
auto device = device_factory.CreateDevice(UNDEFINED, 0, "test.iso");
const unordered_map<string, string> params;
device->Init(params);
EXPECT_NE(nullptr, device);
EXPECT_EQ(SCCD, device->GetType());
EXPECT_TRUE(device->SupportsFile());
@ -247,10 +238,6 @@ TEST(DeviceFactoryTest, SCBR_Device_Defaults)
DeviceFactory device_factory;
auto device = device_factory.CreateDevice(UNDEFINED, 0, "bridge");
const unordered_map<string, string> params;
device->Init(params);
EXPECT_NE(nullptr, device);
EXPECT_EQ(SCBR, device->GetType());
EXPECT_FALSE(device->SupportsFile());
@ -276,10 +263,6 @@ TEST(DeviceFactoryTest, SCDP_Device_Defaults)
DeviceFactory device_factory;
auto device = device_factory.CreateDevice(UNDEFINED, 0, "daynaport");
const unordered_map<string, string> params;
device->Init(params);
EXPECT_NE(nullptr, device);
EXPECT_EQ(SCDP, device->GetType());
EXPECT_FALSE(device->SupportsFile());
@ -304,10 +287,6 @@ TEST(DeviceFactoryTest, SCHS_Device_Defaults)
DeviceFactory device_factory;
auto device = device_factory.CreateDevice(UNDEFINED, 0, "services");
const unordered_map<string, string> params;
device->Init(params);
EXPECT_NE(nullptr, device);
EXPECT_EQ(SCHS, device->GetType());
EXPECT_FALSE(device->SupportsFile());
@ -333,10 +312,6 @@ TEST(DeviceFactoryTest, SCLP_Device_Defaults)
DeviceFactory device_factory;
auto device = device_factory.CreateDevice(UNDEFINED, 0, "printer");
const unordered_map<string, string> params;
device->Init(params);
EXPECT_NE(nullptr, device);
EXPECT_EQ(SCLP, device->GetType());
EXPECT_FALSE(device->SupportsFile());

View File

@ -106,3 +106,40 @@ TEST(ProtobufUtil, ListDevices)
EXPECT_NE(string::npos, device_list.find("Host Services"));
EXPECT_NE(string::npos, device_list.find("SCSI Printer"));
}
TEST(ProtobufUtil, SetProductData)
{
PbDeviceDefinition device;
SetProductData(device, "");
EXPECT_EQ("", device.vendor());
EXPECT_EQ("", device.product());
EXPECT_EQ("", device.revision());
SetProductData(device, "vendor");
EXPECT_EQ("vendor", device.vendor());
EXPECT_EQ("", device.product());
EXPECT_EQ("", device.revision());
SetProductData(device, "vendor:product");
EXPECT_EQ("vendor", device.vendor());
EXPECT_EQ("product", device.product());
EXPECT_EQ("", device.revision());
SetProductData(device, "vendor:product:revision");
EXPECT_EQ("vendor", device.vendor());
EXPECT_EQ("product", device.product());
EXPECT_EQ("revision", device.revision());
}
TEST(ProtobufUtil, SetIdAndLun)
{
PbDeviceDefinition device;
EXPECT_NE("", SetIdAndLun(device, "", 32));
EXPECT_EQ("", SetIdAndLun(device, "1", 32));
EXPECT_EQ(1, device.id());
EXPECT_EQ("", SetIdAndLun(device, "2:0", 32));
EXPECT_EQ(2, device.id());
EXPECT_EQ(0, device.unit());
}

View File

@ -19,6 +19,42 @@ using namespace std;
using namespace rascsi_interface;
using namespace ras_util;
TEST(RasUtilTest, ProcessId)
{
int id = -1;
int lun = -1;
string error = ProcessId("", 32, id, lun);
EXPECT_FALSE(error.empty());
EXPECT_EQ(-1, id);
EXPECT_EQ(-1, lun);
error = ProcessId("0:32", 32, id, lun);
EXPECT_FALSE(error.empty());
EXPECT_EQ(-1, id);
EXPECT_EQ(-1, lun);
error = ProcessId("-1:", 32, id, lun);
EXPECT_FALSE(error.empty());
EXPECT_EQ(-1, id);
EXPECT_EQ(-1, lun);
error = ProcessId("0:-1", 32, id, lun);
EXPECT_FALSE(error.empty());
EXPECT_EQ(-1, id);
EXPECT_EQ(-1, lun);
error = ProcessId("0", 32, id, lun);
EXPECT_TRUE(error.empty());
EXPECT_EQ(0, id);
EXPECT_EQ(0, lun);
error = ProcessId("7:31", 32, id, lun);
EXPECT_TRUE(error.empty());
EXPECT_EQ(7, id);
EXPECT_EQ(31, lun);
}
TEST(RasUtilTest, GetAsUnsignedInt)
{
int result;

View File

@ -10,6 +10,7 @@
#include "mocks.h"
#include "shared/scsi.h"
#include "shared/rascsi_exceptions.h"
#include "devices/device_logger.h"
#include "devices/scsi_command_util.h"
using namespace scsi_command_util;
@ -17,13 +18,14 @@ using namespace scsi_command_util;
TEST(ScsiCommandUtilTest, ModeSelect6)
{
const int LENGTH = 26;
DeviceLogger logger;
vector<int> cdb(6);
vector<uint8_t> buf(LENGTH);
// PF (vendor-specific parameter format) must not fail but be ignored
cdb[1] = 0x00;
ModeSelect(scsi_command::eCmdModeSelect6, cdb, buf, LENGTH, 0);
ModeSelect(logger, scsi_command::eCmdModeSelect6, cdb, buf, LENGTH, 0);
cdb[0] = 0x15;
// PF (standard parameter format)
@ -32,45 +34,50 @@ TEST(ScsiCommandUtilTest, ModeSelect6)
buf[9] = 0x00;
buf[10] = 0x02;
buf[11] = 0x00;
EXPECT_THAT([&] { ModeSelect(scsi_command::eCmdModeSelect6, cdb, buf, LENGTH, 256); }, Throws<scsi_exception>(AllOf(
EXPECT_THAT([&] { ModeSelect(logger, scsi_command::eCmdModeSelect6, cdb, buf, LENGTH, 256); },
Throws<scsi_exception>(AllOf(
Property(&scsi_exception::get_sense_key, sense_key::ILLEGAL_REQUEST),
Property(&scsi_exception::get_asc, asc::INVALID_FIELD_IN_PARAMETER_LIST))))
<< "Requested sector size does not match current sector size";
// Page 0
buf[12] = 0x00;
EXPECT_THAT([&] { ModeSelect(scsi_command::eCmdModeSelect6, cdb, buf, LENGTH, 512); }, Throws<scsi_exception>(AllOf(
EXPECT_THAT([&] { ModeSelect(logger, scsi_command::eCmdModeSelect6, cdb, buf, LENGTH, 512); },
Throws<scsi_exception>(AllOf(
Property(&scsi_exception::get_sense_key, sense_key::ILLEGAL_REQUEST),
Property(&scsi_exception::get_asc, asc::INVALID_FIELD_IN_PARAMETER_LIST))))
<< "Unsupported page 0 was not rejected";
// Page 3 (Format Device Page)
buf[12] = 0x03;
EXPECT_THAT([&] { ModeSelect(scsi_command::eCmdModeSelect6, cdb, buf, LENGTH, 512); }, Throws<scsi_exception>(AllOf(
EXPECT_THAT([&] { ModeSelect(logger, scsi_command::eCmdModeSelect6, cdb, buf, LENGTH, 512); },
Throws<scsi_exception>(AllOf(
Property(&scsi_exception::get_sense_key, sense_key::ILLEGAL_REQUEST),
Property(&scsi_exception::get_asc, asc::INVALID_FIELD_IN_PARAMETER_LIST))))
<< "Requested sector size does not match current sector size";
// Match the requested to the current sector size
buf[24] = 0x02;
EXPECT_THAT([&] { ModeSelect(scsi_command::eCmdModeSelect6, cdb, buf, LENGTH - 1, 512); }, Throws<scsi_exception>(AllOf(
EXPECT_THAT([&] { ModeSelect(logger, scsi_command::eCmdModeSelect6, cdb, buf, LENGTH - 1, 512); },
Throws<scsi_exception>(AllOf(
Property(&scsi_exception::get_sense_key, sense_key::ILLEGAL_REQUEST),
Property(&scsi_exception::get_asc, asc::INVALID_FIELD_IN_PARAMETER_LIST))))
<< "Not enough command parameters";
ModeSelect(scsi_command::eCmdModeSelect6, cdb, buf, LENGTH, 512);
ModeSelect(logger, scsi_command::eCmdModeSelect6, cdb, buf, LENGTH, 512);
}
TEST(ScsiCommandUtilTest, ModeSelect10)
{
const int LENGTH = 30;
DeviceLogger logger;
vector<int> cdb(10);
vector<uint8_t> buf(LENGTH);
// PF (vendor-specific parameter format) must not fail but be ignored
cdb[1] = 0x00;
ModeSelect(scsi_command::eCmdModeSelect10, cdb, buf, LENGTH, 0);
ModeSelect(logger, scsi_command::eCmdModeSelect10, cdb, buf, LENGTH, 0);
// PF (standard parameter format)
cdb[1] = 0x10;
@ -78,33 +85,37 @@ TEST(ScsiCommandUtilTest, ModeSelect10)
buf[13] = 0x00;
buf[14] = 0x02;
buf[15] = 0x00;
EXPECT_THAT([&] { ModeSelect(scsi_command::eCmdModeSelect10, cdb, buf, LENGTH, 256); }, Throws<scsi_exception>(AllOf(
EXPECT_THAT([&] { ModeSelect(logger, scsi_command::eCmdModeSelect10, cdb, buf, LENGTH, 256); },
Throws<scsi_exception>(AllOf(
Property(&scsi_exception::get_sense_key, sense_key::ILLEGAL_REQUEST),
Property(&scsi_exception::get_asc, asc::INVALID_FIELD_IN_PARAMETER_LIST))))
<< "Requested sector size does not match current sector size";
// Page 0
buf[16] = 0x00;
EXPECT_THAT([&] { ModeSelect(scsi_command::eCmdModeSelect10, cdb, buf, LENGTH, 512); }, Throws<scsi_exception>(AllOf(
EXPECT_THAT([&] { ModeSelect(logger, scsi_command::eCmdModeSelect10, cdb, buf, LENGTH, 512); },
Throws<scsi_exception>(AllOf(
Property(&scsi_exception::get_sense_key, sense_key::ILLEGAL_REQUEST),
Property(&scsi_exception::get_asc, asc::INVALID_FIELD_IN_PARAMETER_LIST))))
<< "Unsupported page 0 was not rejected";
// Page 3 (Format Device Page)
buf[16] = 0x03;
EXPECT_THAT([&] { ModeSelect(scsi_command::eCmdModeSelect10, cdb, buf, LENGTH, 512); }, Throws<scsi_exception>(AllOf(
EXPECT_THAT([&] { ModeSelect(logger, scsi_command::eCmdModeSelect10, cdb, buf, LENGTH, 512); },
Throws<scsi_exception>(AllOf(
Property(&scsi_exception::get_sense_key, sense_key::ILLEGAL_REQUEST),
Property(&scsi_exception::get_asc, asc::INVALID_FIELD_IN_PARAMETER_LIST))))
<< "Requested sector size does not match current sector size";
// Match the requested to the current sector size
buf[28] = 0x02;
EXPECT_THAT([&] { ModeSelect(scsi_command::eCmdModeSelect10, cdb, buf, LENGTH - 1, 512); }, Throws<scsi_exception>(AllOf(
EXPECT_THAT([&] { ModeSelect(logger, scsi_command::eCmdModeSelect10, cdb, buf, LENGTH - 1, 512); },
Throws<scsi_exception>(AllOf(
Property(&scsi_exception::get_sense_key, sense_key::ILLEGAL_REQUEST),
Property(&scsi_exception::get_asc, asc::INVALID_FIELD_IN_PARAMETER_LIST))))
<< "Not enough command parameters";
ModeSelect(scsi_command::eCmdModeSelect10, cdb, buf, LENGTH, 512);
ModeSelect(logger, scsi_command::eCmdModeSelect10, cdb, buf, LENGTH, 512);
}
TEST(ScsiCommandUtilTest, EnrichFormatPage)

View File

@ -4,7 +4,7 @@ rascsi \- Emulates SCSI devices using the Raspberry Pi GPIO pins
.SH SYNOPSIS
.B rascsi
[\fB\-F\fR \fIFOLDER\fR]
[\fB\-L\fR \fILOG_LEVEL\fR]
[\fB\-L\fR \fILOG_LEVEL[:ID:[LUN]]\fR]
[\fB\-P\fR \fIACCESS_TOKEN_FILE\fR]
[\fB\-R\fR \fISCAN_DEPTH\fR]
[\fB\-h\fR]
@ -52,8 +52,8 @@ The optional block size, either 512, 1024, 2048 or 4096 bytes. Default size is 5
.BR \-F\fI " " \fIFOLDER
The default folder for image files. For files in this folder no absolute path needs to be specified. The initial default folder is '~/images'.
.TP
.BR \-L\fI " " \fILOG_LEVEL
The rascsi log level (trace, debug, info, warn, err, off). The default log level is 'info'.
.BR \-L\fI " " \fILOG_LEVEL[:ID:[LUN]]
The rascsi log level (trace, debug, info, warn, err, off). The default log level is 'info' for all devices unless a particular device ID and an optional LUN was provided.
.TP
.BR \-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.

View File

@ -6,7 +6,7 @@ NAME
rascsi - Emulates SCSI devices using the Raspberry Pi GPIO pins
SYNOPSIS
rascsi [-F FOLDER] [-L LOG_LEVEL] [-P ACCESS_TOKEN_FILE] [-R
rascsi [-F FOLDER] [-L LOG_LEVEL[:ID:[LUN]]] [-P ACCESS_TOKEN_FILE] [-R
SCAN_DEPTH] [-h] [-n VENDOR:PRODUCT:REVISION] [-p PORT] [-r RE
SERVED_IDS] [-n TYPE] [-v] [-z LOCALE] [-IDn:[u] FILE] [-HDn[:u]
FILE]...
@ -61,9 +61,10 @@ OPTIONS
absolute path needs to be specified. The initial default folder
is '~/images'.
-L LOG_LEVEL
-L LOG_LEVEL[:ID:[LUN]]
The rascsi log level (trace, debug, info, warn, err, off). The
default log level is 'info'.
default log level is 'info' for all devices unless a particular
device ID and an optional LUN was provided.
-P ACCESS_TOKEN_FILE
Enable authentication and read the access token from the speci