RASCSI/src/raspberrypi/devices/primary_device.cpp
Uwe Seimet 1df7cdb1f3
Use vector for INQUIRY data, LUN list can have gaps, made methods const (#713)
* Use vector for INQUIRY data, Inquiry() is const, moved EVPD check

* Moved code

* Fixed warning

* Updated memcpy

* Set length

* Limit result vector size

* Limit result buffer size

* Inquiry() result buffer handling update

* Logging update

* Inquiry cleanup

* NEC drive can also use PrimaryDevice::Inquiry()

* NEC drive is never removable

* Comment update

* Bridge can also use PrimaryDevice::Inquiry()

* Removed unused method argument

* Comment update

* Updated comment

* Updated REQUEST SENSE buffer handling

* Fixed typo

* Fixed typo

* Re-added comment

* Updated additional length handling

* Check for INQUIRY command support first

* Added assertion

* Size handling update

* Renaming

* Renaming

* Removed obsolete casts

* Cleanup

* Moved error codes to scsi_defs namespace

* Fixed ReadDefectData10

* Comment update

* Updated buffer handling

* Data type update

* SendDiagnostic is now const

* Removed obsolete forward declaration

* removed unused enum

* Reduced method visibility

* Renaming

* GetSendDelay() can be const

* Made method const

* Made method const

* Added TODO

* Use iterator

* Made method const

* Revert "Made method const"

This reverts commit 38412b8ddd.

* No need to sort all set/maps

* Do not sort all sets

* Removed more unnecessary sorting

* Cleaned up includes

* More include cleanups

* Updated REPORT LUNS

* LUNs must not be consecutive anymore

* Updated detaching of LUN

* Improvements for devices without LUN 0

* Assume LUN 0 is always present

* Enforce presence of LUN 0

* Updated error handling
2022-03-01 20:25:22 -06:00

215 lines
5.4 KiB
C++

//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "log.h"
#include "controllers/scsidev_ctrl.h"
#include "dispatcher.h"
#include "primary_device.h"
using namespace std;
using namespace scsi_defs;
PrimaryDevice::PrimaryDevice(const string& id) : ScsiPrimaryCommands(), Device(id)
{
ctrl = NULL;
// Mandatory SCSI primary commands
dispatcher.AddCommand(eCmdTestUnitReady, "TestUnitReady", &PrimaryDevice::TestUnitReady);
dispatcher.AddCommand(eCmdInquiry, "Inquiry", &PrimaryDevice::Inquiry);
dispatcher.AddCommand(eCmdReportLuns, "ReportLuns", &PrimaryDevice::ReportLuns);
// Optional commands used by all RaSCSI devices
dispatcher.AddCommand(eCmdRequestSense, "RequestSense", &PrimaryDevice::RequestSense);
}
bool PrimaryDevice::Dispatch(SCSIDEV *controller)
{
return dispatcher.Dispatch(this, controller);
}
void PrimaryDevice::TestUnitReady(SASIDEV *controller)
{
if (!CheckReady()) {
controller->Error();
return;
}
controller->Status();
}
void PrimaryDevice::Inquiry(SASIDEV *controller)
{
// EVPD and page code check
if ((ctrl->cmd[1] & 0x01) || ctrl->cmd[2]) {
controller->Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
return;
}
vector<BYTE> buf = Inquiry();
size_t allocation_length = ctrl->cmd[4] + (ctrl->cmd[3] << 8);
if (allocation_length > buf.size()) {
allocation_length = buf.size();
}
memcpy(ctrl->buffer, buf.data(), allocation_length);
ctrl->length = allocation_length;
int lun = controller->GetEffectiveLun();
// Report if the device does not support the requested LUN
if (!ctrl->unit[lun]) {
LOGTRACE("Reporting LUN %d for device ID %d as not supported", lun, ctrl->device->GetId());
// Signal that the requested LUN does not exist
ctrl->buffer[0] |= 0x7f;
}
controller->DataIn();
}
void PrimaryDevice::ReportLuns(SASIDEV *controller)
{
int allocation_length = (ctrl->cmd[6] << 24) + (ctrl->cmd[7] << 16) + (ctrl->cmd[8] << 8) + ctrl->cmd[9];
BYTE *buf = ctrl->buffer;
memset(buf, 0, allocation_length);
int size = 0;
for (int lun = 0; lun < controller->GetCtrl()->device->GetSupportedLuns(); lun++) {
if (controller->GetCtrl()->unit[lun]) {
size += 8;
buf[size + 7] = lun;
}
}
buf[2] = size >> 8;
buf[3] = size;
size += 8;
ctrl->length = allocation_length < size ? allocation_length : size;
controller->DataIn();
}
void PrimaryDevice::RequestSense(SASIDEV *controller)
{
int lun = controller->GetEffectiveLun();
// Note: According to the SCSI specs the LUN handling for REQUEST SENSE non-existing LUNs do *not* result
// in CHECK CONDITION. Only the Sense Key and ASC are set in order to signal the non-existing LUN.
if (!ctrl->unit[lun]) {
// LUN 0 can be assumed to be present (required to call RequestSense() below)
lun = 0;
controller->Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_LUN);
ctrl->status = 0x00;
return;
}
size_t allocation_length = ctrl->cmd[4];
vector<BYTE> buf = ((PrimaryDevice *)ctrl->unit[lun])->RequestSense(allocation_length);
if (allocation_length > buf.size()) {
allocation_length = buf.size();
}
memcpy(ctrl->buffer, buf.data(), allocation_length);
ctrl->length = allocation_length;
LOGTRACE("%s Status $%02X, Sense Key $%02X, ASC $%02X",__PRETTY_FUNCTION__, ctrl->status, ctrl->buffer[2], ctrl->buffer[12]);
controller->DataIn();
}
bool PrimaryDevice::CheckReady()
{
// Not ready if reset
if (IsReset()) {
SetStatusCode(STATUS_DEVRESET);
SetReset(false);
LOGTRACE("%s Device in reset", __PRETTY_FUNCTION__);
return false;
}
// Not ready if it needs attention
if (IsAttn()) {
SetStatusCode(STATUS_ATTENTION);
SetAttn(false);
LOGTRACE("%s Device in needs attention", __PRETTY_FUNCTION__);
return false;
}
// Return status if not ready
if (!IsReady()) {
SetStatusCode(STATUS_NOTREADY);
LOGTRACE("%s Device not ready", __PRETTY_FUNCTION__);
return false;
}
// Initialization with no error
LOGTRACE("%s Device is ready", __PRETTY_FUNCTION__);
return true;
}
vector<BYTE> PrimaryDevice::Inquiry(int type, int scsi_level, bool is_removable) const
{
vector<BYTE> buf = vector<BYTE>(0x1F + 5);
// Basic data
// buf[0] ... SCSI Device type
// buf[1] ... Bit 7: Removable/not removable
// buf[2] ... SCSI-2 compliant command system
// buf[3] ... SCSI-2 compliant Inquiry response
// buf[4] ... Inquiry additional data
buf[0] = type;
buf[1] = is_removable ? 0x80 : 0x00;
buf[2] = scsi_level;
// Response data format is SCSI-2 for devices supporting SCSI-2 or newer, otherwise it is SCSI-1-CCS
buf[3] = scsi_level >= 2 ? 2 : 1;
buf[4] = 0x1F;
// Padded vendor, product, revision
memcpy(&buf[8], GetPaddedName().c_str(), 28);
return buf;
}
vector<BYTE> PrimaryDevice::RequestSense(int)
{
// Return not ready only if there are no errors
if (GetStatusCode() == STATUS_NOERROR && !IsReady()) {
SetStatusCode(STATUS_NOTREADY);
}
// Set 18 bytes including extended sense data
vector<BYTE> buf(18);
// Current error
buf[0] = 0x70;
buf[2] = GetStatusCode() >> 16;
buf[7] = 10;
buf[12] = GetStatusCode() >> 8;
buf[13] = GetStatusCode();
return buf;
}
bool PrimaryDevice::WriteBytes(BYTE *buf, uint32_t length)
{
LOGERROR("%s Writing bytes is not supported by this device", __PRETTY_FUNCTION__);
return false;
}