mirror of
https://github.com/akuker/RASCSI.git
synced 2024-12-21 08:29:59 +00:00
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:
parent
4fa513090a
commit
454c61ac0d
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -5,14 +5,14 @@
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(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) {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -7,7 +7,6 @@
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "shared/log.h"
|
||||
#include "shared/rascsi_version.h"
|
||||
#include "device.h"
|
||||
#include <cassert>
|
||||
|
@ -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;
|
||||
|
75
cpp/devices/device_logger.cpp
Normal file
75
cpp/devices/device_logger.cpp
Normal 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;
|
||||
}
|
44
cpp/devices/device_logger.h
Normal file
44
cpp/devices/device_logger.h
Normal 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;
|
||||
};
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 }
|
||||
};
|
||||
};
|
||||
|
@ -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':
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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':
|
||||
|
@ -14,7 +14,6 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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()) {
|
||||
|
@ -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>&);
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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&);
|
||||
|
@ -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)
|
||||
|
@ -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());
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user