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

@ -5,14 +5,14 @@
//
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker
// 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 (!GetAsUnsignedInt(l, id)) {
LOGWARN("Invalid device ID 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 (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,38 +61,39 @@ 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
fied file. The access token file must be owned by root and must
Enable authentication and read the access token from the speci
fied file. The access token file must be owned by root and must
be readable by root only.
-R SCAN_DEPTH
Scan for image files recursively, up to a depth of SCAN_DEPTH.
Depth 0 means to ignore any folders within the default image
filder. Be careful when using this option with many sub-folders
Scan for image files recursively, up to a depth of SCAN_DEPTH.
Depth 0 means to ignore any folders within the default image
filder. Be careful when using this option with many sub-folders
in the default image folder. The default depth is 1.
-h Show a help page.
-n VENDOR:PRODUCT:REVISION
Set the vendor, product and revision for the device, to be re
turned with the INQUIRY data. A complete set of name components
Set the vendor, product and revision for the device, to be re
turned with the INQUIRY data. A complete set of name components
must be provided. VENDOR may have up to 8, PRODUCT up to 16, RE
VISION up to 4 characters. Padding with blanks to the maxium
length is automatically applied. Once set the name of a device
VISION up to 4 characters. Padding with blanks to the maxium
length is automatically applied. Once set the name of a device
cannot be changed.
-p PORT
The rascsi server port, default is 6868.
-r RESERVED_IDS
Comma-separated list of IDs to reserve. Pass an empty list in
Comma-separated list of IDs to reserve. Pass an empty list in
order to not reserve anything. -p TYPE The optional case-insen
sitive device type (SAHD, SCHD, SCRM, SCCD, SCMO, SCBR, SCDP,
sitive device type (SAHD, SCHD, SCRM, SCCD, SCMO, SCBR, SCDP,
SCLP, SCHS). If no type is specified for devices that support an
image file, rascsi tries to derive the type from the file exten
sion.
@ -100,20 +101,20 @@ OPTIONS
-v Display the rascsi version.
-z LOCALE
Overrides the default locale for client-faces error messages.
Overrides the default locale for client-faces error messages.
The client can override the locale.
-IDn[:u] FILE
n is the SCSI ID number (0-7). u (0-31) is the optional LUN
n is the SCSI ID number (0-7). u (0-31) is the optional LUN
(logical unit). The default LUN is 0.
FILE is the name of the image file to use for the SCSI device.
FILE is the name of the image file to use for the SCSI device.
For devices that do not support an image file (SCBR, SCDP, SCLP,
SCHS) the filename may have a special meaning or a dummy name
can be provided. For SCBR and SCDP it is an optioinal priori
tized list of network interfaces, an optional IP address and
SCHS) the filename may have a special meaning or a dummy name
can be provided. For SCBR and SCDP it is an optioinal priori
tized list of network interfaces, an optional IP address and
netmask, e.g. "interfaces=eth0,eth1,wlan0:inet=10.10.20.1/24".
For SCLP it is the print command to be used and a reservation
For SCLP it is the print command to be used and a reservation
timeout in seconds, e.g. "cmd=lp -oraw %f:timeout=60".
FILE is the name of the image file to use for the SCSI device.
@ -127,7 +128,7 @@ EXAMPLES
rascsi -ID0 /path/to/harddrive.hda -ID2 /path/to/cdimage.iso
Launch RaSCSI with a removable SCSI drive image as ID 0 and the raw de
vice file /dev/hdb (e.g. a USB stick) and a DaynaPort network adapter
vice file /dev/hdb (e.g. a USB stick) and a DaynaPort network adapter
as ID 6:
rascsi -ID0 -t scrm /dev/hdb -ID6 -t scdp daynaport