mirror of
https://github.com/akuker/RASCSI.git
synced 2024-12-21 08:29:59 +00:00
Say hello to Streamer (tape) device support
Implements the mandatory and a few optional commands for tandberd see https://bitsavers.org/pdf/tandbergData/TDC4100/6047-1_TDC-4100_SCSI-2_Interface_Functional_Specification_Aug1991.pdf for more info. Fixed #480
This commit is contained in:
parent
657d22a491
commit
a940033c0d
@ -4,7 +4,7 @@
|
||||
## CROSS_COMPILE : Specify which compiler toolchain to use.
|
||||
## To cross compile set this accordingly, e.g. to:
|
||||
## arm-linux-gnueabihf-
|
||||
CROSS_COMPILE =
|
||||
CROSS_COMPILE ?=
|
||||
|
||||
CXX = $(CROSS_COMPILE)g++
|
||||
|
||||
|
@ -113,7 +113,7 @@ private:
|
||||
|
||||
int ExtractInitiatorId(int) const;
|
||||
|
||||
using ctrl_t = struct _ctrl_t {
|
||||
struct ctrl_t {
|
||||
// Command data, dynamically resized if required
|
||||
vector<int> cmd = vector<int>(16);
|
||||
|
||||
|
@ -739,9 +739,9 @@ bool ScsiController::XferIn(vector<uint8_t>& buf)
|
||||
case scsi_command::eCmdRead6:
|
||||
case scsi_command::eCmdRead10:
|
||||
case scsi_command::eCmdRead16:
|
||||
// Read from disk
|
||||
// Read from StorageDevice
|
||||
try {
|
||||
SetLength(dynamic_pointer_cast<Disk>(GetDeviceForLun(lun))->Read(buf, GetNext()));
|
||||
SetLength(dynamic_pointer_cast<StorageDevice>(GetDeviceForLun(lun))->Read(buf, GetNext()));
|
||||
}
|
||||
catch(const scsi_exception&) {
|
||||
// If there is an error, go to the status phase
|
||||
@ -819,13 +819,13 @@ bool ScsiController::XferOutBlockOriented(bool cont)
|
||||
break;
|
||||
}
|
||||
|
||||
auto disk = dynamic_pointer_cast<Disk>(device);
|
||||
if (disk == nullptr) {
|
||||
auto storage = dynamic_pointer_cast<StorageDevice>(device);
|
||||
if (storage == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
disk->Write(GetBuffer(), GetNext() - 1);
|
||||
storage->Write(GetBuffer(), GetNext() - 1);
|
||||
}
|
||||
catch(const scsi_exception& e) {
|
||||
Error(e.get_sense_key(), e.get_asc());
|
||||
@ -836,7 +836,7 @@ bool ScsiController::XferOutBlockOriented(bool cont)
|
||||
// If you do not need the next block, end here
|
||||
IncrementNext();
|
||||
if (cont) {
|
||||
SetLength(disk->GetSectorSizeInBytes());
|
||||
SetLength(storage->GetSectorSizeInBytes());
|
||||
ResetOffset();
|
||||
}
|
||||
|
||||
@ -846,15 +846,15 @@ bool ScsiController::XferOutBlockOriented(bool cont)
|
||||
case scsi_command::eCmdVerify10:
|
||||
case scsi_command::eCmdVerify16:
|
||||
{
|
||||
auto disk = dynamic_pointer_cast<Disk>(device);
|
||||
if (disk == nullptr) {
|
||||
auto storage = dynamic_pointer_cast<StorageDevice>(device);
|
||||
if (storage == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If you do not need the next block, end here
|
||||
IncrementNext();
|
||||
if (cont) {
|
||||
SetLength(disk->GetSectorSizeInBytes());
|
||||
SetLength(storage->GetSectorSizeInBytes());
|
||||
ResetOffset();
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ class ScsiController : public AbstractController
|
||||
|
||||
const int DEFAULT_BUFFER_SIZE = 0x1000;
|
||||
|
||||
using scsi_t = struct _scsi_t {
|
||||
struct scsi_t {
|
||||
// Synchronous transfer
|
||||
bool syncenable; // Synchronous transfer possible
|
||||
uint8_t syncperiod = MAX_SYNC_PERIOD; // Synchronous transfer period
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "scsi_daynaport.h"
|
||||
#include "host_services.h"
|
||||
#include "device_factory.h"
|
||||
#include "scsi_streamer.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace piscsi_util;
|
||||
@ -60,6 +61,9 @@ shared_ptr<PrimaryDevice> DeviceFactory::CreateDevice(PbDeviceType type, int lun
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SCST:
|
||||
device = make_shared<SCSIST>(lun);
|
||||
break;
|
||||
|
||||
case SCRM:
|
||||
device = make_shared<SCSIHD>(lun, true, scsi_level::scsi_2);
|
||||
|
@ -46,7 +46,9 @@ private:
|
||||
{ "is1", SCCD },
|
||||
{ "iso", SCCD },
|
||||
{ "cdr", SCCD },
|
||||
{ "toast", SCCD }
|
||||
{ "toast", SCCD },
|
||||
{ "tar", SCST },
|
||||
{ "tap", SCST },
|
||||
};
|
||||
|
||||
const inline static unordered_map<string, PbDeviceType, piscsi_util::StringHash, equal_to<>> DEVICE_MAPPING = {
|
||||
|
@ -36,7 +36,6 @@ bool Disk::Init(const param_map& params)
|
||||
AddCommand(scsi_command::eCmdWrite6, [this] { Write6(); });
|
||||
AddCommand(scsi_command::eCmdSeek6, [this] { Seek6(); });
|
||||
AddCommand(scsi_command::eCmdStartStop, [this] { StartStopUnit(); });
|
||||
AddCommand(scsi_command::eCmdPreventAllowMediumRemoval, [this]{ PreventAllowMediumRemoval(); });
|
||||
AddCommand(scsi_command::eCmdReadCapacity10, [this] { ReadCapacity10(); });
|
||||
AddCommand(scsi_command::eCmdRead10, [this] { Read10(); });
|
||||
AddCommand(scsi_command::eCmdWrite10, [this] { Write10(); });
|
||||
@ -232,18 +231,7 @@ void Disk::StartStopUnit()
|
||||
EnterStatusPhase();
|
||||
}
|
||||
|
||||
void Disk::PreventAllowMediumRemoval()
|
||||
{
|
||||
CheckReady();
|
||||
|
||||
const bool lock = GetController()->GetCmdByte(4) & 0x01;
|
||||
|
||||
LogTrace(lock ? "Locking medium" : "Unlocking medium");
|
||||
|
||||
SetLocked(lock);
|
||||
|
||||
EnterStatusPhase();
|
||||
}
|
||||
|
||||
void Disk::SynchronizeCache()
|
||||
{
|
||||
@ -682,32 +670,6 @@ tuple<bool, uint64_t, uint32_t> Disk::CheckAndGetStartAndCount(access_mode mode)
|
||||
return tuple(true, start, count);
|
||||
}
|
||||
|
||||
uint32_t Disk::CalculateShiftCount(uint32_t size_in_bytes)
|
||||
{
|
||||
const auto& it = shift_counts.find(size_in_bytes);
|
||||
return it != shift_counts.end() ? it->second : 0;
|
||||
}
|
||||
|
||||
uint32_t Disk::GetSectorSizeInBytes() const
|
||||
{
|
||||
return size_shift_count ? 1 << size_shift_count : 0;
|
||||
}
|
||||
|
||||
void Disk::SetSectorSizeInBytes(uint32_t size_in_bytes)
|
||||
{
|
||||
if (!GetSupportedSectorSizes().contains(size_in_bytes)) {
|
||||
throw io_exception("Invalid sector size of " + to_string(size_in_bytes) + " byte(s)");
|
||||
}
|
||||
|
||||
size_shift_count = CalculateShiftCount(size_in_bytes);
|
||||
assert(size_shift_count);
|
||||
}
|
||||
|
||||
uint32_t Disk::GetConfiguredSectorSize() const
|
||||
{
|
||||
return configured_sector_size;
|
||||
}
|
||||
|
||||
bool Disk::SetConfiguredSectorSize(uint32_t configured_size)
|
||||
{
|
||||
if (!supported_sector_sizes.contains(configured_size)) {
|
||||
|
@ -34,12 +34,6 @@ class Disk : public StorageDevice, private ScsiBlockCommands
|
||||
|
||||
unique_ptr<DiskCache> cache;
|
||||
|
||||
unordered_set<uint32_t> supported_sector_sizes;
|
||||
uint32_t configured_sector_size = 0;
|
||||
|
||||
// Sector size shift count (9=512, 10=1024, 11=2048, 12=4096)
|
||||
uint32_t size_shift_count = 0;
|
||||
|
||||
uint64_t sector_read_count = 0;
|
||||
uint64_t sector_write_count = 0;
|
||||
|
||||
@ -49,7 +43,7 @@ class Disk : public StorageDevice, private ScsiBlockCommands
|
||||
public:
|
||||
|
||||
Disk(PbDeviceType type, int lun, const unordered_set<uint32_t>& s)
|
||||
: StorageDevice(type, lun), supported_sector_sizes(s) {}
|
||||
: StorageDevice(type, lun, s) {}
|
||||
~Disk() override = default;
|
||||
|
||||
bool Init(const param_map&) override;
|
||||
@ -59,13 +53,11 @@ public:
|
||||
|
||||
bool Eject(bool) override;
|
||||
|
||||
virtual void Write(span<const uint8_t>, uint64_t);
|
||||
void Write(span<const uint8_t>, uint64_t) override;
|
||||
|
||||
virtual int Read(span<uint8_t> , uint64_t);
|
||||
int Read(span<uint8_t> , uint64_t) override;
|
||||
|
||||
uint32_t GetSectorSizeInBytes() const;
|
||||
bool IsSectorSizeConfigurable() const { return supported_sector_sizes.size() > 1; }
|
||||
const auto& GetSupportedSectorSizes() const { return supported_sector_sizes; }
|
||||
bool SetConfiguredSectorSize(uint32_t);
|
||||
void FlushCache() override;
|
||||
|
||||
@ -75,7 +67,6 @@ private:
|
||||
|
||||
// Commands covered by the SCSI specifications (see https://www.t10.org/drafts.htm)
|
||||
void StartStopUnit();
|
||||
void PreventAllowMediumRemoval();
|
||||
void SynchronizeCache();
|
||||
void ReadDefectData10() const;
|
||||
virtual void Read6() { Read(RW6); }
|
||||
@ -100,14 +91,12 @@ private:
|
||||
void ReadCapacity16_ReadLong16();
|
||||
|
||||
void ValidateBlockAddress(access_mode) const;
|
||||
|
||||
tuple<bool, uint64_t, uint32_t> CheckAndGetStartAndCount(access_mode) const;
|
||||
|
||||
int ModeSense6(cdb_t, vector<uint8_t>&) const override;
|
||||
int ModeSense10(cdb_t, vector<uint8_t>&) const override;
|
||||
|
||||
static inline const unordered_map<uint32_t, uint32_t> shift_counts =
|
||||
{ { 512, 9 }, { 1024, 10 }, { 2048, 11 }, { 4096, 12 } };
|
||||
|
||||
protected:
|
||||
|
||||
void SetUpCache(off_t, bool = false);
|
||||
@ -119,10 +108,4 @@ protected:
|
||||
virtual void AddDrivePage(map<int, vector<byte>>&, bool) const;
|
||||
void AddCachePage(map<int, vector<byte>>&, bool) const;
|
||||
|
||||
unordered_set<uint32_t> GetSectorSizes() const;
|
||||
void SetSectorSizeInBytes(uint32_t);
|
||||
uint32_t GetSectorSizeShiftCount() const { return size_shift_count; }
|
||||
void SetSectorSizeShiftCount(uint32_t count) { size_shift_count = count; }
|
||||
uint32_t GetConfiguredSectorSize() const;
|
||||
static uint32_t CalculateShiftCount(uint32_t);
|
||||
};
|
||||
|
@ -114,7 +114,7 @@ void ModePageDevice::ModeSense10() const
|
||||
EnterDataInPhase();
|
||||
}
|
||||
|
||||
void ModePageDevice::ModeSelect(scsi_command, cdb_t, span<const uint8_t>, int) const
|
||||
void ModePageDevice::ModeSelect(scsi_command, cdb_t, span<const uint8_t>, int)
|
||||
{
|
||||
// There is no default implementation of MODE SELECT
|
||||
throw scsi_exception(sense_key::illegal_request, asc::invalid_command_operation_code);
|
||||
|
@ -23,7 +23,7 @@ public:
|
||||
|
||||
bool Init(const param_map&) override;
|
||||
|
||||
virtual void ModeSelect(scsi_defs::scsi_command, cdb_t, span<const uint8_t>, int) const;
|
||||
virtual void ModeSelect(scsi_defs::scsi_command, cdb_t, span<const uint8_t>, int);
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -38,7 +38,7 @@ public:
|
||||
virtual bool Init(const param_map&);
|
||||
virtual void CleanUp() {
|
||||
// Override if cleanup work is required for a derived device
|
||||
};
|
||||
}
|
||||
|
||||
virtual void Dispatch(scsi_command);
|
||||
|
||||
|
@ -104,12 +104,7 @@ void scsi_command_util::AddAppleVendorModePage(map<int, vector<byte>>& pages, bo
|
||||
}
|
||||
}
|
||||
|
||||
int scsi_command_util::GetInt24(span <const int> buf, int offset)
|
||||
{
|
||||
assert(buf.size() > static_cast<size_t>(offset) + 2);
|
||||
|
||||
return (buf[offset] << 16) | (buf[offset + 1] << 8) | buf[offset + 2];
|
||||
}
|
||||
|
||||
uint32_t scsi_command_util::GetInt32(span <const int> buf, int offset)
|
||||
{
|
||||
|
@ -31,7 +31,7 @@ namespace scsi_command_util
|
||||
assert(buf.size() > static_cast<size_t>(offset) + 1);
|
||||
|
||||
return (static_cast<int>(buf[offset]) << 8) | buf[offset + 1];
|
||||
};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void SetInt16(vector<T>& buf, int offset, int value)
|
||||
@ -52,8 +52,22 @@ namespace scsi_command_util
|
||||
buf[offset + 2] = static_cast<T>(value >> 8);
|
||||
buf[offset + 3] = static_cast<T>(value);
|
||||
}
|
||||
template<typename T>
|
||||
void SetInt24(vector<T>& buf, int offset, uint32_t value)
|
||||
{
|
||||
assert(buf.size() > static_cast<size_t>(offset) + 3);
|
||||
|
||||
int GetInt24(span<const int>, int);
|
||||
buf[offset + 0] = static_cast<T>(value >> 16);
|
||||
buf[offset + 1] = static_cast<T>(value >> 8);
|
||||
buf[offset + 2] = static_cast<T>(value);
|
||||
}
|
||||
|
||||
inline int GetInt24(const auto buf, int offset)
|
||||
{
|
||||
assert(buf.size() > static_cast<size_t>(offset) + 2);
|
||||
|
||||
return (int(buf[offset]) << 16) | (int(buf[offset + 1]) << 8) | buf[offset + 2];
|
||||
}
|
||||
uint32_t GetInt32(span <const int>, int);
|
||||
uint64_t GetInt64(span<const int>, int);
|
||||
void SetInt64(vector<uint8_t>&, int, uint64_t);
|
||||
|
923
cpp/devices/scsi_streamer.cpp
Normal file
923
cpp/devices/scsi_streamer.cpp
Normal file
@ -0,0 +1,923 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2024 BogDan Vatra <bogdan@kde.org>
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "scsi_streamer.h"
|
||||
|
||||
#include "scsi_command_util.h"
|
||||
|
||||
using namespace scsi_command_util;
|
||||
|
||||
|
||||
File::~File()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
bool File::open(string_view filename)
|
||||
{
|
||||
close();
|
||||
file = fopen(filename.data(), "r+");
|
||||
if (!file)
|
||||
throw scsi_exception(sense_key::illegal_request, asc::medium_not_present);
|
||||
return file != nullptr;
|
||||
}
|
||||
|
||||
void File::rewind()
|
||||
{
|
||||
seek(0, SEEK_SET);
|
||||
}
|
||||
|
||||
size_t File::read(uint8_t *buff, size_t size, size_t count)
|
||||
{
|
||||
if (!file)
|
||||
throw scsi_exception(sense_key::illegal_request, asc::medium_not_present);
|
||||
return checkSize(fread(buff, size, count, file));
|
||||
}
|
||||
|
||||
size_t File::write(const uint8_t* buff, size_t size, size_t count)
|
||||
{
|
||||
if (!file)
|
||||
throw scsi_exception(sense_key::illegal_request, asc::medium_not_present);
|
||||
return checkSize(fwrite(buff, size, count, file));
|
||||
}
|
||||
|
||||
void File::seek(long offset, int origin)
|
||||
{
|
||||
if (!file)
|
||||
throw scsi_exception(sense_key::illegal_request, asc::medium_not_present);
|
||||
if (fseek(file, offset, origin))
|
||||
throw scsi_exception(sense_key::medium_error);
|
||||
}
|
||||
|
||||
long File::tell()
|
||||
{
|
||||
if (!file)
|
||||
throw scsi_exception(sense_key::illegal_request, asc::medium_not_present);
|
||||
|
||||
return checkSize(ftell(file));
|
||||
}
|
||||
|
||||
void File::close()
|
||||
{
|
||||
if (file) {
|
||||
fclose(file);
|
||||
file = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SCSIST::SCSIST(int lun)
|
||||
: StorageDevice(SCST, lun, {512})
|
||||
{
|
||||
SetProtectable(true);
|
||||
SetRemovable(true);
|
||||
SetLockable(true);
|
||||
SupportsSaveParameters(true);
|
||||
SetVendor("TANDBERG"); // Masquerade as Tandberg
|
||||
SetProduct(" TDC Streamer");
|
||||
}
|
||||
|
||||
bool SCSIST::Init(const param_map &pm)
|
||||
{
|
||||
// Mandatory commands
|
||||
// | INQUIRY | 12h | M | 8.2.5 |
|
||||
// | MODE SELECT(6) | 15h | M | 8.2.8 |
|
||||
// | MODE SENSE(6) | 1Ah | M | 8.2.10 |
|
||||
// | RELEASE UNIT | 17h | M | 10.2.9 |
|
||||
// | REQUEST SENSE | 03h | M | 8.2.14 |
|
||||
// | RESERVE UNIT | 16h | M | 10.2.10 |
|
||||
// | SEND DIAGNOSTIC | 1Dh | M | 8.2.15 |
|
||||
// | TEST UNIT READY | 00h | M | 8.2.16 |
|
||||
|
||||
// Optional commands
|
||||
// | MODE SELECT(10) | 55h | O | 8.2.9 |
|
||||
// | MODE SENSE(10) | 5Ah | O | 8.2.11 |
|
||||
// | PREVENT ALLOW MEDIUM REMOVAL | 1Eh | O | 9.2.4 |
|
||||
if (!StorageDevice::Init(pm))
|
||||
return false;
|
||||
|
||||
// Mandatory commands
|
||||
// | ERASE | 19h | M | 10.2.1 |
|
||||
AddCommand(scsi_command::eCmdErase, [this] { Erase(); });
|
||||
|
||||
// | READ | 08h | M | 10.2.4 |
|
||||
AddCommand(scsi_command::eCmdRead6, [this] { Read6(); });
|
||||
|
||||
// | READ BLOCK LIMITS | 05h | M | 10.2.5 |
|
||||
AddCommand(scsi_command::eCmdReadBlockLimits, [this] { ReadBlockLimits(); });
|
||||
|
||||
// | REWIND | 01h | M | 10.2.11 |
|
||||
AddCommand(scsi_command::eCmdRezero, [this] { Rewind(); });
|
||||
|
||||
// | SPACE | 11h | M | 10.2.12 |
|
||||
AddCommand(scsi_command::eCmdSpace, [this] { Space(); });
|
||||
|
||||
// | WRITE | 0Ah | M | 10.2.14 |
|
||||
AddCommand(scsi_command::eCmdWrite6, [this] { Write6(); });
|
||||
|
||||
// | WRITE FILEMARKS | 10h | M | 10.2.15 |
|
||||
AddCommand(scsi_command::eCmdWriteFilemarks, [this] { WriteFilemarks(); });
|
||||
|
||||
// Optional commands
|
||||
|
||||
// | LOAD UNLOAD | 1Bh | O | 10.2.2 |
|
||||
AddCommand(scsi_command::eCmdStartStop, [this] { LoadUnload(); });
|
||||
|
||||
// | READ POSITION | 34h | O | 10.2.6 |
|
||||
AddCommand(scsi_command::eCmdReadPosition, [this] { ReadPosition(); });
|
||||
|
||||
// | VERIFY | 13h | O | 10.2.13 |
|
||||
AddCommand(scsi_command::eCmdVerify, [this] { Verify(); });
|
||||
|
||||
/*
|
||||
| CHANGE DEFINITION | 40h | O | 8.2.1 |
|
||||
| COMPARE | 39h | O | 8.2.2 |
|
||||
| COPY | 18h | O | 8.2.3 |
|
||||
| COPY AND VERIFY | 3Ah | O | 8.2.4 |
|
||||
| LOCATE | 2Bh | O | 10.2.3 |
|
||||
| LOG SELECT | 4Ch | O | 8.2.6 |
|
||||
| LOG SENSE | 4Dh | O | 8.2.7 |
|
||||
| READ BUFFER | 3Ch | O | 8.2.12 |
|
||||
| READ REVERSE | 0Fh | O | 10.2.7 |
|
||||
| RECEIVE DIAGNOSTIC RESULTS | 1Ch | O | 8.2.13 |
|
||||
| RECOVER BUFFERED DATA | 14h | O | 10.2.8 |
|
||||
| WRITE BUFFER | 3Bh | O | 8.2.17 |
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
void SCSIST::CleanUp()
|
||||
{
|
||||
file.close();
|
||||
StorageDevice::CleanUp();
|
||||
}
|
||||
|
||||
std::vector<uint8_t> SCSIST::InquiryInternal() const
|
||||
{
|
||||
return HandleInquiry(scsi_defs::device_type::sad, scsi_level::scsi_2, true);
|
||||
}
|
||||
|
||||
void SCSIST::ModeSelect(scsi_command cmd, cdb_t cdb, span<const uint8_t> buff, int length)
|
||||
{
|
||||
/*
|
||||
+=====-========-========-========-========-========-========-========-========+
|
||||
| Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
|Byte | | | | | | | | |
|
||||
|=====+=======================================================================|
|
||||
| 0 | Operation code (15h) |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 1 | Logical unit number | PF | Reserved | SP |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 2 | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 3 | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 4 | Parameter list length |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 5 | Control |
|
||||
+=============================================================================+
|
||||
*/
|
||||
if (cmd != scsi_command::eCmdModeSelect6 || length < 12)
|
||||
throw scsi_exception(sense_key::illegal_request, asc::invalid_command_operation_code);
|
||||
|
||||
// If the PF-bit is set to zero, the
|
||||
// Drive will not accept any Mode Pages in the parameter list; only the
|
||||
// Header List and a Block Descriptor List will be accepted. If the Drive
|
||||
// receives a parameter list containing bytes beyond the Header List and a
|
||||
// Block Descriptor List, it will terminate the MODE SELECT command
|
||||
// with CHECK CONDITION and set the Sense Key to ILLEGAL REQUEST and the AS/AQ sense bytes to PARAMETER LIST LENGTH
|
||||
// ERROR. The Error Code will be set to E$STE_PLEN.
|
||||
bool pf = cdb[1] & 0x10;
|
||||
|
||||
|
||||
// A Save Page (SP) bit of zero indicates that the Drive will perform the
|
||||
// specified MODE SELECT operation, but not save any mode parameters.
|
||||
// A SP bit of one indicates that the Drive will perform the specified
|
||||
// MODE SELECT operation and also save all saveable MODE SELECT
|
||||
// parameters received during the DATA OUT phase.
|
||||
bool sp = cdb[1] & 0x01;// this one is tricky, should we have two sets of settings ?!?!?
|
||||
|
||||
|
||||
// This field specifies the length in bytes of the MODE SELECT parameter
|
||||
// list that will be transferred from the Initiator to the Drive during the
|
||||
// DATA OUT phase. A Parameter List Length of zero indicates that no
|
||||
// data will be transferred. No mode selection parameters are then
|
||||
// changed. A parameter list length must never result in the truncation of
|
||||
// any header, descriptor or page of parameters.
|
||||
uint8_t params_list_len = cdb[4] & 0xff;
|
||||
if ((!pf && params_list_len != 12) || params_list_len < 12 || params_list_len > length)
|
||||
throw scsi_exception(sense_key::illegal_request, asc::parameter_list_length_error);
|
||||
|
||||
// Header List
|
||||
|
||||
// Check Block Descriptor Length
|
||||
if (buff[3] != 0x08)
|
||||
throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_parameter_list);
|
||||
|
||||
// Block Descriptor List
|
||||
uint32_t block_length = GetInt24(buff, 9);
|
||||
if (sp)
|
||||
SetSectorSizeInBytes(block_length);
|
||||
}
|
||||
|
||||
void SCSIST::Erase()
|
||||
{
|
||||
/*
|
||||
Table 175 - ERASE command
|
||||
+=====-========-========-========-========-========-========-========-========+
|
||||
| Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
|Byte | | | | | | | | |
|
||||
|=====+=======================================================================|
|
||||
| 0 | Operation code (19h) |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 1 | Logical unit number | Reserved | Immed | Long |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 2 | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 3 | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 4 | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 5 | Control |
|
||||
+=============================================================================+
|
||||
*/
|
||||
file.rewind();
|
||||
EnterStatusPhase();
|
||||
}
|
||||
|
||||
int SCSIST::ModeSense6(cdb_t cdb, std::vector<uint8_t> &buf) const
|
||||
{
|
||||
/*
|
||||
Table 54 - MODE SENSE(6) command
|
||||
+=====-========-========-========-========-========-========-========-========+
|
||||
| Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
|Byte | | | | | | | | |
|
||||
|=====+=======================================================================|
|
||||
| 0 | Operation code (1Ah) |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 1 | Logical unit number |Reserved| DBD | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 2 | PC | Page code |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 3 | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 4 | Allocation length |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 5 | Control |
|
||||
+=============================================================================+
|
||||
A disable block descriptors (DBD) bit of zero indicates that the target may
|
||||
return zero or more block descriptors in the returned MODE SENSE data
|
||||
(see 8.3.3), at the target's discretion. A DBD bit of one specifies that the
|
||||
target shall not return any block descriptors in the returned MODE SENSE data.
|
||||
The page control (PC) field defines the type of mode parameter values to be
|
||||
returned in the mode pages. The page control field is defined in table 55.
|
||||
|
||||
|
||||
Table 55 - Page control field
|
||||
+=======-=====================-============+
|
||||
| Code | Type of parameter | Subclause |
|
||||
|-------+---------------------+------------|
|
||||
| 00b | Current values | 8.2.10.1 |
|
||||
| 01b | Changeable values | 8.2.10.2 |
|
||||
| 10b | Default values | 8.2.10.3 |
|
||||
| 11b | Saved values | 8.2.10.4 |
|
||||
+==========================================+
|
||||
|
||||
|
||||
Table 56 - Mode page code usage for all devices
|
||||
+=============-==================================================-============+
|
||||
| Page code | Description | Subclause |
|
||||
|-------------+--------------------------------------------------+------------|
|
||||
| 00h | Vendor-specific (does not require page format) | |
|
||||
| 01h - 1Fh | See specific device-types | |
|
||||
| 20h - 3Eh | Vendor-specific (page format required) | |
|
||||
| 3Fh | Return all mode pages | |
|
||||
+=============================================================================+
|
||||
*/
|
||||
// Get length, clear buffer
|
||||
const auto length = static_cast<int>(min(buf.size(), static_cast<size_t>(cdb[4])));
|
||||
fill_n(buf.begin(), length, 0);
|
||||
|
||||
// DEVICE SPECIFIC PARAMETER
|
||||
if (IsProtected()) {
|
||||
buf[2] = 0x80;
|
||||
}
|
||||
|
||||
// Basic information
|
||||
int size = 4;
|
||||
|
||||
// Add block descriptor if DBD is 0
|
||||
if ((cdb[1] & 0x08) == 0) {
|
||||
// Mode parameter header, block descriptor length
|
||||
buf[3] = 0x08;
|
||||
|
||||
// Only if ready
|
||||
if (IsReady()) {
|
||||
// Short LBA mode parameter block descriptor (number of blocks and block length)
|
||||
SetInt32(buf, 4, static_cast<uint32_t>(GetBlockCount()));
|
||||
SetInt32(buf, 8, GetSectorSizeInBytes());
|
||||
}
|
||||
|
||||
size = 12;
|
||||
}
|
||||
|
||||
size = AddModePages(cdb, buf, size, length, 255);
|
||||
|
||||
buf[0] = (uint8_t)size;
|
||||
|
||||
return size;
|
||||
|
||||
}
|
||||
|
||||
int SCSIST::ModeSense10(cdb_t cdb, std::vector<uint8_t> &buf) const
|
||||
{
|
||||
/*
|
||||
Table 57 - MODE SENSE(10) command
|
||||
+=====-========-========-========-========-========-========-========-========+
|
||||
| Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
|Byte | | | | | | | | |
|
||||
|=====+=======================================================================|
|
||||
| 0 | Operation code (5Ah) |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 1 | Logical unit number |Reserved| DBD | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 2 | PC | Page code |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 3 | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 4 | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 5 | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 6 | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 7 | (MSB) |
|
||||
|-----+--- Allocation length ---|
|
||||
| 8 | (LSB) |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 9 | Control |
|
||||
+=============================================================================+
|
||||
*/
|
||||
// Get length, clear buffer
|
||||
const auto length = static_cast<int>(min(buf.size(), static_cast<size_t>(GetInt16(cdb, 7))));
|
||||
fill_n(buf.begin(), length, 0);
|
||||
|
||||
// DEVICE SPECIFIC PARAMETER
|
||||
if (IsProtected()) {
|
||||
buf[3] = 0x80;
|
||||
}
|
||||
|
||||
// Basic Information
|
||||
int size = 8;
|
||||
|
||||
// Add block descriptor if DBD is 0, only if ready
|
||||
if ((cdb[1] & 0x08) == 0 && IsReady()) {
|
||||
uint64_t disk_blocks = GetBlockCount();
|
||||
uint32_t disk_size = GetSectorSizeInBytes();
|
||||
|
||||
// Check LLBAA for short or long block descriptor
|
||||
if ((cdb[1] & 0x10) == 0 || disk_blocks <= 0xFFFFFFFF) {
|
||||
// Mode parameter header, block descriptor length
|
||||
buf[7] = 0x08;
|
||||
|
||||
// Short LBA mode parameter block descriptor (number of blocks and block length)
|
||||
SetInt32(buf, 8, static_cast<uint32_t>(disk_blocks));
|
||||
SetInt32(buf, 12, disk_size);
|
||||
|
||||
size = 16;
|
||||
}
|
||||
else {
|
||||
// Mode parameter header, LONGLBA
|
||||
buf[4] = 0x01;
|
||||
|
||||
// Mode parameter header, block descriptor length
|
||||
buf[7] = 0x10;
|
||||
|
||||
// Long LBA mode parameter block descriptor (number of blocks and block length)
|
||||
SetInt64(buf, 8, disk_blocks);
|
||||
SetInt32(buf, 20, disk_size);
|
||||
|
||||
size = 24;
|
||||
}
|
||||
}
|
||||
|
||||
size = AddModePages(cdb, buf, size, length, 65535);
|
||||
|
||||
SetInt16(buf, 0, size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void SCSIST::Read6()
|
||||
{
|
||||
/*
|
||||
Table 178 - READ command
|
||||
+=====-========-========-========-========-========-========-========-========+
|
||||
| Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
|Byte | | | | | | | | |
|
||||
|=====+=======================================================================|
|
||||
| 0 | Operation code (08h) |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 1 | Logical unit number | Reserved | SILI | Fixed |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 2 | (MSB) |
|
||||
|-----+--- ---|
|
||||
| 3 | Transfer length |
|
||||
|-----+--- ---|
|
||||
| 4 | (LSB) |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 5 | Control |
|
||||
+=============================================================================+
|
||||
*/
|
||||
CheckReady();
|
||||
const auto &cmd = GetController()->GetCmd();
|
||||
int fixed = cmd[1] & 1;
|
||||
bool sili = cmd[1] & 2;
|
||||
uint32_t transfer_length = GetInt24(cmd, 2);
|
||||
if (fixed)
|
||||
transfer_length *= GetSectorSizeInBytes();
|
||||
|
||||
uint32_t blocks = transfer_length / GetSectorSizeInBytes();
|
||||
if (!sili && ((uint32_t(file.tell()) + transfer_length > GetFileSize())
|
||||
|| (!fixed && (transfer_length % GetSectorSizeInBytes() != 0)))) {
|
||||
throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb);
|
||||
}
|
||||
|
||||
GetController()->SetBlocks(blocks);
|
||||
GetController()->AllocateBuffer(GetSectorSizeInBytes());
|
||||
auto len = file.read(GetController()->GetBuffer().data(), GetSectorSizeInBytes());
|
||||
GetController()->SetLength(GetSectorSizeInBytes());
|
||||
|
||||
LogTrace("Length is " + to_string(len));
|
||||
|
||||
// Set next block
|
||||
GetController()->SetNext(file.tell() / GetSectorSizeInBytes());
|
||||
|
||||
EnterDataInPhase();
|
||||
}
|
||||
|
||||
void SCSIST::ReadBlockLimits() const
|
||||
{
|
||||
/*
|
||||
Table 179 - READ BLOCK LIMITS command
|
||||
+=====-========-========-========-========-========-========-========-========+
|
||||
| Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
|Byte | | | | | | | | |
|
||||
|=====+=======================================================================|
|
||||
| 0 | Operation code (05h) |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 1 | Logical unit number | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 2 | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 3 | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 4 | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 5 | Control |
|
||||
+=============================================================================+
|
||||
|
||||
Table 180 - READ BLOCK LIMITS data
|
||||
+=====-========-========-========-========-========-========-========-========+
|
||||
| Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
|Byte | | | | | | | | |
|
||||
|=====+=======================================================================|
|
||||
| 0 | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 1 | (MSB) |
|
||||
|-----+--- ---|
|
||||
| 2 | Maximum block length limit |
|
||||
|-----+--- ---|
|
||||
| 3 | (LSB) |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 4 | (MSB) |
|
||||
|-----+--- Minimum block length limit ---|
|
||||
| 5 | (LSB) |
|
||||
+=============================================================================+
|
||||
*/
|
||||
GetController()->AllocateBuffer(6);
|
||||
fill_n(GetController()->GetBuffer().begin(), 6, 0);
|
||||
SetInt24(GetController()->GetBuffer(), 1, GetMaxSupportedSectorSize());
|
||||
SetInt16(GetController()->GetBuffer(), 4, GetMinSupportedSectorSize());
|
||||
GetController()->SetBlocks(1);
|
||||
GetController()->SetLength(6);
|
||||
EnterDataInPhase();
|
||||
}
|
||||
|
||||
void SCSIST::Rewind()
|
||||
{
|
||||
/*
|
||||
Table 187 - REWIND command
|
||||
+=====-========-========-========-========-========-========-========-========+
|
||||
| Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
|Byte | | | | | | | | |
|
||||
|=====+=======================================================================|
|
||||
| 0 | Operation code (01h) |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 1 | Logical unit number | Reserved | Immed |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 2 | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 3 | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 4 | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 5 | Control |
|
||||
+=============================================================================+
|
||||
*/
|
||||
file.rewind();
|
||||
EnterStatusPhase();
|
||||
}
|
||||
|
||||
void SCSIST::Space()
|
||||
{
|
||||
/*
|
||||
Table 188 - SPACE command
|
||||
+=====-========-========-========-========-========-========-========-========+
|
||||
| Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
|Byte | | | | | | | | |
|
||||
|=====+=======================================================================|
|
||||
| 0 | Operation (11h) |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 1 | Logical unit number | Reserved | Code |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 2 | (MSB) |
|
||||
|-----+--- ---|
|
||||
| 3 | Count |
|
||||
|-----+--- ---|
|
||||
| 4 | (LSB) |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 5 | Control |
|
||||
+=============================================================================+
|
||||
The code field is defined in table 189.
|
||||
|
||||
Table 189 - Code field definition
|
||||
+=============-========================-=============+
|
||||
| Code | Description | Support |
|
||||
|-------------+------------------------+-------------|
|
||||
| 000b | Blocks | Mandatory |
|
||||
| 001b | Filemarks | Mandatory |
|
||||
| 010b | Sequential filemarks | Optional |
|
||||
| 011b | End-of-data | Optional |
|
||||
| 100b | Setmarks | Optional |
|
||||
| 101b | Sequential setmarks | Optional |
|
||||
| 110b - 111b | Reserved | |
|
||||
+====================================================+
|
||||
*/
|
||||
uint8_t code = GetController()->GetCmd()[1] & 0x07;
|
||||
uint32_t count = GetInt24(GetController()->GetCmd(), 2);
|
||||
switch (code) {
|
||||
case 0: // Blocks
|
||||
if (GetFileSize() >= count * GetSectorSizeInBytes()) {
|
||||
file.seek(count * GetSectorSizeInBytes(), SEEK_SET);
|
||||
break;
|
||||
}
|
||||
// Fall through
|
||||
case 1: // Filemarks
|
||||
case 2: // Sequential filemarks
|
||||
case 3: // End-of-data
|
||||
case 4: // Setmarks
|
||||
case 5: // Sequential setmarks
|
||||
default:
|
||||
// TODO Add proper implementation
|
||||
GetController()->Error(sense_key::blank_check, asc::no_additional_sense_information, status::check_condition);
|
||||
EnterStatusPhase();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SCSIST::Write6()
|
||||
{
|
||||
/*
|
||||
Table 191 - WRITE command
|
||||
+=====-========-========-========-========-========-========-========-========+
|
||||
| Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
|Byte | | | | | | | | |
|
||||
|=====+=======================================================================|
|
||||
| 0 | Operation code (0Ah) |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 1 | Logical unit number | Reserved | Fixed |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 2 | (MSB) |
|
||||
|-----+--- ---|
|
||||
| 3 | Transfer length |
|
||||
|-----+--- ---|
|
||||
| 4 | (LSB) |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 5 | Control |
|
||||
+=============================================================================+
|
||||
*/
|
||||
CheckReady();
|
||||
if (IsProtected()) {
|
||||
throw scsi_exception(sense_key::data_protect, asc::write_protected);
|
||||
}
|
||||
bool fixed = GetController()->GetCmd()[1] & 1;
|
||||
uint32_t length = GetInt24(GetController()->GetCmd(),2);
|
||||
|
||||
if (!fixed) {
|
||||
if (length != GetSectorSizeInBytes()) {
|
||||
throw scsi_exception(sense_key::illegal_request, asc::invalid_field_in_cdb);
|
||||
}
|
||||
} else {
|
||||
length *= GetSectorSizeInBytes();
|
||||
}
|
||||
GetController()->SetBlocks(length / GetSectorSizeInBytes());
|
||||
GetController()->SetLength(length);
|
||||
|
||||
// Set next block
|
||||
GetController()->SetNext(file.tell() / GetSectorSizeInBytes() + 1);
|
||||
|
||||
EnterDataOutPhase();
|
||||
}
|
||||
|
||||
void SCSIST::WriteFilemarks()
|
||||
{
|
||||
/*
|
||||
Table 191 - WRITE command
|
||||
+=====-========-========-========-========-========-========-========-========+
|
||||
| Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
|Byte | | | | | | | | |
|
||||
|=====+=======================================================================|
|
||||
| 0 | Operation code (0Ah) |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 1 | Logical unit number | Reserved | Fixed |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 2 | (MSB) |
|
||||
|-----+--- ---|
|
||||
| 3 | Transfer length |
|
||||
|-----+--- ---|
|
||||
| 4 | (LSB) |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 5 | Control |
|
||||
+=============================================================================+
|
||||
*/
|
||||
// TODO Add proper implementation
|
||||
EnterStatusPhase();
|
||||
}
|
||||
|
||||
void SCSIST::LoadUnload()
|
||||
{
|
||||
/*
|
||||
Table 176 - LOAD UNLOAD command
|
||||
+=====-========-========-========-========-========-========-========-========+
|
||||
| Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
|Byte | | | | | | | | |
|
||||
|=====+=======================================================================|
|
||||
| 0 | Operation code (1Bh) |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 1 | Logical unit number | Reserved | Immed |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 2 | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 3 | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 4 | Reserved | EOT | Reten | Load |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 5 | Control |
|
||||
+=============================================================================+
|
||||
*/
|
||||
bool load = GetController()->GetCmd()[4] & 1;
|
||||
bool eot = GetController()->GetCmd()[4] & 4;
|
||||
if (load) {
|
||||
file.rewind();
|
||||
} else if (eot) {
|
||||
file.seek(0, SEEK_END);
|
||||
}
|
||||
if (load & eot){
|
||||
GetController()->Error(sense_key::illegal_request, asc::no_additional_sense_information, status::check_condition);
|
||||
}
|
||||
EnterStatusPhase();
|
||||
}
|
||||
|
||||
void SCSIST::ReadPosition()
|
||||
{
|
||||
/*
|
||||
Table 181 - READ POSITION command
|
||||
+=====-========-========-========-========-========-========-========-========+
|
||||
| Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
|Byte | | | | | | | | |
|
||||
|=====+=======================================================================|
|
||||
| 0 | Operation code (34h) |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 1 | Logical unit number | Reserved | BT |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 2 | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 3 | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 4 | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 5 | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 6 | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 7 | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 8 | Reserved |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 9 | Control |
|
||||
+=============================================================================+
|
||||
*/
|
||||
GetController()->AllocateBuffer(20);
|
||||
fill_n(GetController()->GetBuffer().begin(), 6, 0);
|
||||
size_t lba= file.tell() / GetSectorSizeInBytes();
|
||||
auto &buf = GetController()->GetBuffer();
|
||||
if (!lba)
|
||||
buf[0] |= 0x80;
|
||||
else if (lba >= GetBlockCount())
|
||||
buf[0] |= 0x40;
|
||||
|
||||
SetInt32(GetController()->GetBuffer(), 4, lba);
|
||||
SetInt32(GetController()->GetBuffer(), 8, lba);
|
||||
|
||||
EnterDataInPhase();
|
||||
}
|
||||
|
||||
void SCSIST::Verify()
|
||||
{
|
||||
/*
|
||||
Table 190 - VERIFY command
|
||||
+=====-========-========-========-========-========-========-========-========+
|
||||
| Bit| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|
||||
|Byte | | | | | | | | |
|
||||
|=====+=======================================================================|
|
||||
| 0 | Operation code (13h) |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 1 | Logical unit number | Reserved | Immed | BytCmp | Fixed |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 2 | (MSB) |
|
||||
|-----+--- ---|
|
||||
| 3 | Verification length |
|
||||
|-----+--- ---|
|
||||
| 4 | (LSB) |
|
||||
|-----+-----------------------------------------------------------------------|
|
||||
| 5 | Control |
|
||||
+=============================================================================+
|
||||
*/
|
||||
// TODO Add proper implementation
|
||||
EnterStatusPhase();
|
||||
}
|
||||
|
||||
void SCSIST::SetUpModePages(map<int, vector<byte>>& pages, int page, bool changeable) const
|
||||
{
|
||||
// Page code 0 is returning the Header List followed by the Block Descriptor List - a total of 12 bytes.
|
||||
// When selecting Page Code OOh the DBD bit is ignored.
|
||||
if (page == 0x00) {
|
||||
AddBlockDescriptorPage(pages, changeable);
|
||||
}
|
||||
|
||||
|
||||
// Page code 1 (read-write error recovery)
|
||||
if (page == 0x01) {
|
||||
AddErrorPage(pages, changeable);
|
||||
}
|
||||
|
||||
// Page code 2 (disconnect/reconnect)
|
||||
if (page == 0x02) {
|
||||
AddReconnectPage(pages, changeable);
|
||||
}
|
||||
|
||||
// Page code 0x10 (Device Configuration)
|
||||
if (page == 0x10) {
|
||||
AddDevicePage(pages, changeable);
|
||||
}
|
||||
|
||||
// Page code 0x11 (Medium Partition)
|
||||
if (page == 0x11) {
|
||||
AddMediumPartitionPage(pages, changeable);
|
||||
}
|
||||
|
||||
// Page code 0x20 (Miscellaneous)
|
||||
if (page == 0x20) {
|
||||
AddMiscellaneousPage(pages, changeable);
|
||||
}
|
||||
|
||||
// Page (vendor special)
|
||||
AddVendorPage(pages, page, changeable);
|
||||
}
|
||||
|
||||
void SCSIST::Write(span<const uint8_t> buff, uint64_t block)
|
||||
{
|
||||
assert(block < GetBlockCount());
|
||||
|
||||
CheckReady();
|
||||
|
||||
file.seek(block * GetSectorSizeInBytes(), SEEK_SET);
|
||||
file.write(buff.data(), GetSectorSizeInBytes());
|
||||
}
|
||||
|
||||
int SCSIST::Read(span<uint8_t>buff, uint64_t block)
|
||||
{
|
||||
assert(block < GetBlockCount());
|
||||
file.seek(block * GetSectorSizeInBytes(), SEEK_SET);
|
||||
file.read(buff.data(), GetSectorSizeInBytes());
|
||||
return GetSectorSizeInBytes();
|
||||
}
|
||||
|
||||
|
||||
void SCSIST::Open()
|
||||
{
|
||||
assert(!IsReady());
|
||||
// Sector size (default 512 bytes) and number of blocks
|
||||
SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 512);
|
||||
off_t size = GetFileSize();
|
||||
if (size % GetSectorSizeInBytes() != 0) {
|
||||
size = (size / GetSectorSizeInBytes() + 1) * GetSectorSizeInBytes();
|
||||
}
|
||||
SetBlockCount(static_cast<uint32_t>(size >> GetSectorSizeShiftCount()));
|
||||
file.open(GetFilename());
|
||||
StorageDevice::ValidateFile();
|
||||
}
|
||||
|
||||
void SCSIST::AddBlockDescriptorPage(std::map<int, std::vector<byte> > &pages, bool) const
|
||||
{
|
||||
// Page Code OOh is returning the Header List followed by the Block Descriptor List - a total of 12 bytes.
|
||||
// When selecting Page Code OOh the DBD bit is ignored.
|
||||
vector<byte> buf(12);
|
||||
buf[2] = (byte)0; // Buffered Mode | Tape Speed
|
||||
buf[3] = (byte)8; // Block descriptor length
|
||||
buf[4] = (byte)0; // Density Code
|
||||
SetInt24(buf, 5, GetBlockCount()); // Number of Blocks
|
||||
buf[8] = (byte)0; // Reserved
|
||||
SetInt24(buf, 9, GetSectorSizeInBytes()); // Block Length
|
||||
pages[0] = buf;
|
||||
}
|
||||
|
||||
void SCSIST::AddErrorPage(map<int, vector<byte> > &pages, bool) const
|
||||
{
|
||||
vector<byte> buf(12);
|
||||
|
||||
buf[0] = (byte)0x01; // Page code
|
||||
buf[1] = (byte)0x0a; // Page length
|
||||
buf[2] = (byte)0x00; // RCR, EXB, TB, PER, DTE
|
||||
buf[3] = (byte)0x01; // Read retry count
|
||||
buf[8] = (byte)0x01; // Write retry count
|
||||
|
||||
pages[1] = buf;
|
||||
}
|
||||
|
||||
void SCSIST::AddReconnectPage(map<int, vector<byte> > &pages, bool) const
|
||||
{
|
||||
vector<byte> buf(16);
|
||||
|
||||
buf[0] = (byte)0x02; // Page code
|
||||
buf[1] = (byte)0x0e; // Page length
|
||||
buf[2] = (byte)0x00; // Read Buffer Full Ratio
|
||||
buf[3] = (byte)0x00; // Write Buffer Empty Ratio
|
||||
|
||||
pages[2] = buf;
|
||||
}
|
||||
|
||||
void SCSIST::AddDevicePage(map<int, vector<byte> > &pages, bool) const
|
||||
{
|
||||
vector<byte> buf(16);
|
||||
|
||||
buf[0] = (byte)0x10; // Page code
|
||||
buf[1] = (byte)0x0e; // Page length
|
||||
buf[2] = (byte)0x00; // CAP, CAF, Active Format
|
||||
buf[3] = (byte)0x00; // Active Partition
|
||||
buf[4] = (byte)0x00; // Write Buffer Full Ratio
|
||||
buf[5] = (byte)0x00; // Read Buffer Empty Ratio
|
||||
SetInt16(buf, 6, 0x0000); // Write Buffer Empty Ratio
|
||||
buf[8] = (byte)0b11100000; // DBR, BIS, RSMK, AVC, SOCF, RBO, REW
|
||||
buf[9] = (byte)0x00; // Gap Size
|
||||
buf[10] = (byte)0b00111000; // EOD Defined, EEG, SEW, RESERVED
|
||||
buf[11] = buf[12] = buf[13] = (byte)0x00; // Buffer Size
|
||||
buf[14] = (byte)0x00; // Select Data Compression Algorithm
|
||||
pages[0x10] = buf;
|
||||
}
|
||||
|
||||
void SCSIST::AddMediumPartitionPage(map<int, vector<byte> > &pages, bool) const
|
||||
{
|
||||
vector<byte> buf(8);
|
||||
|
||||
buf[0] = (byte)0x11; // Page code
|
||||
buf[1] = (byte)0x06; // Page length
|
||||
buf[2] = (byte)0x01; // Maximum Additional Partitions
|
||||
buf[3] = (byte)0x00; // Additional Partition Length
|
||||
|
||||
pages[0x11] = buf;
|
||||
}
|
||||
|
||||
void SCSIST::AddMiscellaneousPage(map<int, vector<byte> > &pages, bool) const
|
||||
{
|
||||
vector<byte> buf(12);
|
||||
|
||||
buf[0] = (byte)0x12; // Page code
|
||||
buf[1] = (byte)0x0a; // Page length
|
||||
SetInt16(buf, 2, 0x0001); // Maximum Additional Partitions
|
||||
buf[4] = (byte)0x18; // ASI, Target Sense Length
|
||||
buf[5] = (byte)0x08; // Copy Threshold
|
||||
buf[6] = (byte)0x00; // Load Function
|
||||
buf[7] = (byte)0x00; // Power-Up Auto Load/Retension Delay
|
||||
buf[8] = (byte)0b10000000; // DTM1, DTM2, SPEW, EOWR EADS, BSY, RD, FAST
|
||||
buf[9] = (byte)0x00; // LED Function
|
||||
buf[10] = (byte)0x00; // PSEW Position
|
||||
pages[0x12] = buf;
|
||||
}
|
83
cpp/devices/scsi_streamer.h
Normal file
83
cpp/devices/scsi_streamer.h
Normal file
@ -0,0 +1,83 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator PiSCSI
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2024 BogDan Vatra <bogdan@kde.org>
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "storage_device.h"
|
||||
#include <cstdio>
|
||||
|
||||
class File {
|
||||
FILE *file = nullptr;
|
||||
inline auto checkSize(auto size) const {
|
||||
if (size < 0)
|
||||
throw scsi_exception(sense_key::medium_error);
|
||||
return size;
|
||||
}
|
||||
|
||||
public:
|
||||
~File();
|
||||
bool open(std::string_view filename);
|
||||
void rewind();
|
||||
size_t read(uint8_t* buff, size_t size, size_t count = 1);
|
||||
size_t write(const uint8_t *buff, size_t size, size_t count = 1);
|
||||
void seek(long offset, int origin);
|
||||
long tell();
|
||||
void close();
|
||||
};
|
||||
|
||||
class SCSIST: public StorageDevice
|
||||
{
|
||||
public:
|
||||
SCSIST(int lun);
|
||||
|
||||
public:
|
||||
bool Init(const param_map &pm) final;
|
||||
void CleanUp() final;
|
||||
|
||||
private:
|
||||
void Erase();
|
||||
void Read6();
|
||||
void ReadBlockLimits() const;
|
||||
void Rewind();
|
||||
void Space();
|
||||
void Write6();
|
||||
void WriteFilemarks();
|
||||
|
||||
void LoadUnload();
|
||||
void ReadPosition();
|
||||
void Verify();
|
||||
|
||||
int ModeSense6(cdb_t cdb, std::vector<uint8_t> &buf) const final;
|
||||
int ModeSense10(cdb_t, std::vector<uint8_t> &) const final;
|
||||
|
||||
// PrimaryDevice interface
|
||||
protected:
|
||||
std::vector<uint8_t> InquiryInternal() const final;
|
||||
|
||||
// ModePageDevice interface
|
||||
protected:
|
||||
void ModeSelect(scsi_defs::scsi_command, cdb_t, span<const uint8_t>, int) final;
|
||||
void SetUpModePages(std::map<int, std::vector<byte> > &, int, bool) const final;
|
||||
|
||||
// StorageDevice interface
|
||||
private:
|
||||
void Write(span<const uint8_t>, uint64_t) final;
|
||||
int Read(span<uint8_t> , uint64_t) final;
|
||||
|
||||
public:
|
||||
void Open() final;
|
||||
void AddBlockDescriptorPage(std::map<int, std::vector<byte> > &, bool) const;
|
||||
void AddErrorPage(map<int, vector<byte> > &, bool) const;
|
||||
void AddReconnectPage(map<int, vector<byte> > &, bool) const;
|
||||
void AddDevicePage(map<int, vector<byte> > &, bool) const;
|
||||
void AddMediumPartitionPage(map<int, vector<byte> > &, bool) const;
|
||||
void AddMiscellaneousPage(map<int, vector<byte> > &, bool) const;
|
||||
private:
|
||||
File file;
|
||||
};
|
@ -82,7 +82,7 @@ vector<uint8_t> SCSIHD::InquiryInternal() const
|
||||
return HandleInquiry(device_type::direct_access, scsi_level, IsRemovable());
|
||||
}
|
||||
|
||||
void SCSIHD::ModeSelect(scsi_command cmd, cdb_t cdb, span<const uint8_t> buf, int length) const
|
||||
void SCSIHD::ModeSelect(scsi_command cmd, cdb_t cdb, span<const uint8_t> buf, int length)
|
||||
{
|
||||
if (const string result = scsi_command_util::ModeSelect(cmd, cdb, buf, length, 1 << GetSectorSizeShiftCount());
|
||||
!result.empty()) {
|
||||
|
@ -37,7 +37,7 @@ public:
|
||||
|
||||
// Commands
|
||||
vector<uint8_t> InquiryInternal() const override;
|
||||
void ModeSelect(scsi_defs::scsi_command, cdb_t, span<const uint8_t>, int) const override;
|
||||
void ModeSelect(scsi_defs::scsi_command, cdb_t, span<const uint8_t>, int) override;
|
||||
|
||||
void AddFormatPage(map<int, vector<byte>>&, bool) const override;
|
||||
void AddVendorPage(map<int, vector<byte>>&, int, bool) const override;
|
||||
|
@ -88,7 +88,7 @@ void SCSIMO::AddOptionPage(map<int, vector<byte>>& pages, bool) const
|
||||
// Do not report update blocks
|
||||
}
|
||||
|
||||
void SCSIMO::ModeSelect(scsi_command cmd, cdb_t cdb, span<const uint8_t> buf, int length) const
|
||||
void SCSIMO::ModeSelect(scsi_command cmd, cdb_t cdb, span<const uint8_t> buf, int length)
|
||||
{
|
||||
if (const string result = scsi_command_util::ModeSelect(cmd, cdb, buf, length, 1 << GetSectorSizeShiftCount());
|
||||
!result.empty()) {
|
||||
|
@ -32,7 +32,7 @@ public:
|
||||
void Open() override;
|
||||
|
||||
vector<uint8_t> InquiryInternal() const override;
|
||||
void ModeSelect(scsi_defs::scsi_command, cdb_t, span<const uint8_t>, int) const override;
|
||||
void ModeSelect(scsi_defs::scsi_command, cdb_t, span<const uint8_t>, int) override;
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -8,18 +8,29 @@
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "shared/piscsi_exceptions.h"
|
||||
#include "scsi_command_util.h"
|
||||
#include "storage_device.h"
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace filesystem;
|
||||
using namespace scsi_command_util;
|
||||
|
||||
StorageDevice::StorageDevice(PbDeviceType type, int lun) : ModePageDevice(type, lun)
|
||||
StorageDevice::StorageDevice(PbDeviceType type, int lun, const unordered_set<uint32_t> &s)
|
||||
: ModePageDevice(type, lun)
|
||||
, supported_sector_sizes(s)
|
||||
{
|
||||
SupportsFile(true);
|
||||
SetStoppable(true);
|
||||
}
|
||||
|
||||
bool StorageDevice::Init(const param_map &pm)
|
||||
{
|
||||
ModePageDevice::Init(pm);
|
||||
AddCommand(scsi_command::eCmdPreventAllowMediumRemoval, [this]{ PreventAllowMediumRemoval(); });
|
||||
return true;
|
||||
}
|
||||
|
||||
void StorageDevice::CleanUp()
|
||||
{
|
||||
UnreserveFile();
|
||||
@ -93,6 +104,12 @@ id_set StorageDevice::GetIdsForReservedFile(const string& file)
|
||||
return { -1, -1 };
|
||||
}
|
||||
|
||||
uint32_t StorageDevice::CalculateShiftCount(uint32_t size_in_bytes)
|
||||
{
|
||||
const auto& it = shift_counts.find(size_in_bytes);
|
||||
return it != shift_counts.end() ? it->second : 0;
|
||||
}
|
||||
|
||||
void StorageDevice::UnreserveAll()
|
||||
{
|
||||
reserved_files.clear();
|
||||
@ -117,3 +134,60 @@ off_t StorageDevice::GetFileSize() const
|
||||
throw io_exception("Can't get size of '" + filename.string() + "': " + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void StorageDevice::PreventAllowMediumRemoval()
|
||||
{
|
||||
CheckReady();
|
||||
|
||||
const bool lock = GetController()->GetCmdByte(4) & 0x01;
|
||||
|
||||
LogTrace(lock ? "Locking medium" : "Unlocking medium");
|
||||
|
||||
SetLocked(lock);
|
||||
|
||||
EnterStatusPhase();
|
||||
}
|
||||
|
||||
uint32_t StorageDevice::GetSectorSizeInBytes() const
|
||||
{
|
||||
return size_shift_count ? 1 << size_shift_count : 0;
|
||||
}
|
||||
|
||||
void StorageDevice::SetSectorSizeInBytes(uint32_t size_in_bytes)
|
||||
{
|
||||
if (!GetSupportedSectorSizes().contains(size_in_bytes)) {
|
||||
throw io_exception("Invalid sector size of " + to_string(size_in_bytes) + " byte(s)");
|
||||
}
|
||||
|
||||
size_shift_count = CalculateShiftCount(size_in_bytes);
|
||||
assert(size_shift_count);
|
||||
}
|
||||
|
||||
uint32_t StorageDevice::GetMinSupportedSectorSize() const
|
||||
{
|
||||
uint32_t res = 0;
|
||||
for (const auto& s : supported_sector_sizes) {
|
||||
if (!res || s < res) {
|
||||
res = s;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
uint32_t StorageDevice::GetMaxSupportedSectorSize() const
|
||||
{
|
||||
uint32_t res = 0;
|
||||
for (const auto& s : supported_sector_sizes) {
|
||||
if (s > res) {
|
||||
res = s;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
uint32_t StorageDevice::GetConfiguredSectorSize() const
|
||||
{
|
||||
return configured_sector_size;
|
||||
}
|
||||
|
||||
|
||||
|
@ -23,9 +23,10 @@ class StorageDevice : public ModePageDevice
|
||||
{
|
||||
public:
|
||||
|
||||
StorageDevice(PbDeviceType, int);
|
||||
StorageDevice(PbDeviceType, int, const unordered_set<uint32_t>&);
|
||||
~StorageDevice() override = default;
|
||||
|
||||
bool Init(const param_map&) override;
|
||||
void CleanUp() override;
|
||||
|
||||
virtual void Open() = 0;
|
||||
@ -49,6 +50,20 @@ public:
|
||||
{ reserved_files = r; }
|
||||
static id_set GetIdsForReservedFile(const string&);
|
||||
|
||||
static uint32_t CalculateShiftCount(uint32_t);
|
||||
uint32_t GetSectorSizeInBytes() const;
|
||||
void SetSectorSizeInBytes(uint32_t);
|
||||
const auto& GetSupportedSectorSizes() const { return supported_sector_sizes; }
|
||||
uint32_t GetMinSupportedSectorSize() const;
|
||||
uint32_t GetMaxSupportedSectorSize() const;
|
||||
|
||||
unordered_set<uint32_t> GetSectorSizes() const;
|
||||
uint32_t GetSectorSizeShiftCount() const { return size_shift_count; }
|
||||
void SetSectorSizeShiftCount(uint32_t count) { size_shift_count = count; }
|
||||
uint32_t GetConfiguredSectorSize() const;
|
||||
|
||||
virtual void Write(span<const uint8_t>, uint64_t) = 0;
|
||||
virtual int Read(span<uint8_t> , uint64_t) = 0;
|
||||
protected:
|
||||
|
||||
void ValidateFile();
|
||||
@ -59,6 +74,21 @@ protected:
|
||||
|
||||
off_t GetFileSize() const;
|
||||
|
||||
protected:
|
||||
// Sector size shift count (9=512, 10=1024, 11=2048, 12=4096)
|
||||
uint32_t size_shift_count = 0;
|
||||
|
||||
unordered_set<uint32_t> supported_sector_sizes;
|
||||
|
||||
static inline const unordered_map<uint32_t, uint32_t> shift_counts =
|
||||
{ { 512, 9 }, { 1024, 10 }, { 2048, 11 }, { 4096, 12 } };
|
||||
|
||||
uint32_t configured_sector_size = 0;
|
||||
|
||||
private:
|
||||
void PreventAllowMediumRemoval();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
bool IsReadOnlyFile() const;
|
||||
|
@ -41,6 +41,8 @@ enum PbDeviceType {
|
||||
SCHS = 8;
|
||||
// Printer device
|
||||
SCLP = 9;
|
||||
// Streamer (tape) device
|
||||
SCST = 10;
|
||||
}
|
||||
|
||||
// piscsi remote operations, returning PbResult
|
||||
|
@ -39,7 +39,7 @@ void ScsiCtl::Banner(const vector<char *>& args) const
|
||||
<< " where ID[:LUN] ID := {0-" << (ControllerManager::GetScsiIdMax() - 1) << "},"
|
||||
<< " LUN := {0-" << (ControllerManager::GetScsiLunMax() - 1) << "}, default is 0\n"
|
||||
<< " CMD := {attach|detach|insert|eject|protect|unprotect|show}\n"
|
||||
<< " TYPE := {schd|scrm|sccd|scmo|scbr|scdp} or convenience type {hd|rm|mo|cd|bridge|daynaport}\n"
|
||||
<< " TYPE := {schd|scrm|sccd|scmo|scbr|scdp|scst} or convenience type {hd|rm|mo|cd|bridge|daynaport|streamer}\n"
|
||||
<< " BLOCK_SIZE := {512|1024|2048|4096) bytes per hard disk drive block\n"
|
||||
<< " NAME := name of device to attach (VENDOR:PRODUCT:REVISION)\n"
|
||||
<< " FILE|PARAM := image file path or device-specific parameter\n"
|
||||
|
@ -49,6 +49,7 @@ enum class phase_t {
|
||||
|
||||
enum class device_type {
|
||||
direct_access = 0,
|
||||
sad = 1,
|
||||
printer = 2,
|
||||
processor = 3,
|
||||
cd_rom = 5,
|
||||
@ -61,6 +62,7 @@ enum class scsi_command {
|
||||
eCmdRezero = 0x01,
|
||||
eCmdRequestSense = 0x03,
|
||||
eCmdFormatUnit = 0x04,
|
||||
eCmdReadBlockLimits= 0x05,
|
||||
eCmdReassignBlocks = 0x07,
|
||||
eCmdRead6 = 0x08,
|
||||
// Bridge specific command
|
||||
@ -79,10 +81,14 @@ enum class scsi_command {
|
||||
// DaynaPort specific command
|
||||
eCmdEnableInterface = 0x0E,
|
||||
eCmdSynchronizeBuffer = 0x10,
|
||||
eCmdWriteFilemarks = 0x10,
|
||||
eCmdSpace = 0x11,
|
||||
eCmdInquiry = 0x12,
|
||||
eCmdVerify = 0x13,
|
||||
eCmdModeSelect6 = 0x15,
|
||||
eCmdReserve6 = 0x16,
|
||||
eCmdRelease6 = 0x17,
|
||||
eCmdErase = 0x19,
|
||||
eCmdModeSense6 = 0x1A,
|
||||
eCmdStartStop = 0x1B,
|
||||
eCmdStopPrint = 0x1B,
|
||||
@ -93,6 +99,7 @@ enum class scsi_command {
|
||||
eCmdWrite10 = 0x2A,
|
||||
eCmdSeek10 = 0x2B,
|
||||
eCmdVerify10 = 0x2F,
|
||||
eCmdReadPosition = 0x34,
|
||||
eCmdSynchronizeCache10 = 0x35,
|
||||
eCmdReadDefectData10 = 0x37,
|
||||
eCmdReadLong10 = 0x3E,
|
||||
@ -123,6 +130,7 @@ enum class sense_key {
|
||||
illegal_request = 0x05,
|
||||
unit_attention = 0x06,
|
||||
data_protect = 0x07,
|
||||
blank_check = 0x08,
|
||||
aborted_command = 0x0b
|
||||
};
|
||||
|
||||
@ -130,6 +138,7 @@ enum class asc {
|
||||
no_additional_sense_information = 0x00,
|
||||
write_fault = 0x03,
|
||||
read_fault = 0x11,
|
||||
parameter_list_length_error = 0x1a,
|
||||
invalid_command_operation_code = 0x20,
|
||||
lba_out_of_range = 0x21,
|
||||
invalid_field_in_cdb = 0x24,
|
||||
|
@ -46,7 +46,7 @@ TEST(DeviceFactoryTest, GetExtensionMapping)
|
||||
DeviceFactory device_factory;
|
||||
|
||||
auto mapping = device_factory.GetExtensionMapping();
|
||||
EXPECT_EQ(12, mapping.size());
|
||||
EXPECT_EQ(14, mapping.size());
|
||||
EXPECT_EQ(SCHD, mapping["hd1"]);
|
||||
EXPECT_EQ(SCHD, mapping["hds"]);
|
||||
EXPECT_EQ(SCHD, mapping["hda"]);
|
||||
@ -59,6 +59,8 @@ TEST(DeviceFactoryTest, GetExtensionMapping)
|
||||
EXPECT_EQ(SCCD, mapping["is1"]);
|
||||
EXPECT_EQ(SCCD, mapping["cdr"]);
|
||||
EXPECT_EQ(SCCD, mapping["toast"]);
|
||||
EXPECT_EQ(SCST, mapping["tar"]);
|
||||
EXPECT_EQ(SCST, mapping["tap"]);
|
||||
}
|
||||
|
||||
TEST(DeviceFactoryTest, UnknownDeviceType)
|
||||
|
@ -301,11 +301,14 @@ public:
|
||||
|
||||
MOCK_METHOD(vector<uint8_t>, InquiryInternal, (), (const));
|
||||
MOCK_METHOD(void, Open, (), (override));
|
||||
MOCK_METHOD(void ,Write, (span<const uint8_t>, uint64_t), (override));
|
||||
MOCK_METHOD(int , Read, (span<uint8_t>, uint64_t), (override));
|
||||
|
||||
MOCK_METHOD(int, ModeSense6, (span<const int>, vector<uint8_t>&), (const override));
|
||||
MOCK_METHOD(int, ModeSense10, (span<const int>, vector<uint8_t>&), (const override));
|
||||
MOCK_METHOD(void, SetUpModePages, ((map<int, vector<byte>>&), int, bool), (const override));
|
||||
|
||||
MockStorageDevice() : StorageDevice(UNDEFINED, 0) {}
|
||||
MockStorageDevice() : StorageDevice(UNDEFINED, 0, {512}) {}
|
||||
~MockStorageDevice() override = default;
|
||||
};
|
||||
|
||||
|
@ -171,7 +171,7 @@ TEST(PiscsiResponseTest, GetDeviceTypesInfo)
|
||||
|
||||
PbDeviceTypesInfo info;
|
||||
response.GetDeviceTypesInfo(info);
|
||||
EXPECT_EQ(8, info.properties().size());
|
||||
EXPECT_EQ(9, info.properties().size());
|
||||
}
|
||||
|
||||
TEST(PiscsiResponseTest, GetServerInfo)
|
||||
@ -254,5 +254,5 @@ TEST(PiscsiResponseTest, GetMappingInfo)
|
||||
|
||||
PbMappingInfo info;
|
||||
response.GetMappingInfo(info);
|
||||
EXPECT_EQ(12, info.mapping().size());
|
||||
EXPECT_EQ(14, info.mapping().size());
|
||||
}
|
||||
|
@ -356,7 +356,7 @@ function configureTokenAuth() {
|
||||
|
||||
echo "$TOKEN" > "$SECRET_FILE"
|
||||
|
||||
# Make the secret file owned and only readable by root
|
||||
# Make the secret file owned and only readable by root
|
||||
sudo chown root:root "$SECRET_FILE"
|
||||
sudo chmod 600 "$SECRET_FILE"
|
||||
|
||||
@ -391,7 +391,7 @@ function installWebInterfaceService() {
|
||||
|
||||
if [ ! -z "$TOKEN" ]; then
|
||||
sudo sed -i "8 i ExecStart=$WEB_INSTALL_PATH/start.sh --password=$TOKEN" "$SYSTEMD_PATH/piscsi-web.service"
|
||||
# Make the service file readable by root only, to protect the token string
|
||||
# Make the service file readable by root only, to protect the token string
|
||||
sudo chmod 600 "$SYSTEMD_PATH/piscsi-web.service"
|
||||
echo "Granted access to the Web Interface with the token password that you configured for PiSCSI."
|
||||
else
|
||||
@ -587,14 +587,14 @@ function setupWiredNetworking() {
|
||||
echo "Setting up wired network..."
|
||||
|
||||
if [[ -z $HEADLESS ]]; then
|
||||
LAN_INTERFACE=`ip -o addr show scope link | awk '{split($0, a); print $2}' | grep 'eth\|enx' | head -n 1`
|
||||
LAN_INTERFACE=`ip -o addr show scope link | awk '{split($0, a); print $2}' | grep 'eth\|enx' | head -n 1`
|
||||
else
|
||||
LAN_INTERFACE="eth0"
|
||||
LAN_INTERFACE="eth0"
|
||||
fi
|
||||
|
||||
if [[ -z "$LAN_INTERFACE" ]]; then
|
||||
echo "No usable wired network interfaces detected. Have you already enabled the bridge? Aborting..."
|
||||
return 1
|
||||
echo "No usable wired network interfaces detected. Have you already enabled the bridge? Aborting..."
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "Network interface '$LAN_INTERFACE' will be configured for network forwarding with DHCP."
|
||||
@ -664,14 +664,14 @@ function setupWirelessNetworking() {
|
||||
ROUTING_ADDRESS=$NETWORK.0/$CIDR
|
||||
|
||||
if [[ -z $HEADLESS ]]; then
|
||||
WLAN_INTERFACE=`ip -o addr show scope link | awk '{split($0, a); print $2}' | grep 'wlan\|wlx' | head -n 1`
|
||||
WLAN_INTERFACE=`ip -o addr show scope link | awk '{split($0, a); print $2}' | grep 'wlan\|wlx' | head -n 1`
|
||||
else
|
||||
WLAN_INTERFACE="wlan0"
|
||||
WLAN_INTERFACE="wlan0"
|
||||
fi
|
||||
|
||||
if [[ -z "$WLAN_INTERFACE" ]]; then
|
||||
echo "No usable wireless network interfaces detected. Have you already enabled the bridge? Aborting..."
|
||||
return 1
|
||||
echo "No usable wireless network interfaces detected. Have you already enabled the bridge? Aborting..."
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "Network interface '$WLAN_INTERFACE' will be configured for network forwarding with static IP assignment."
|
||||
@ -1063,7 +1063,7 @@ function installPiscsiScreen() {
|
||||
sudo sed -i /^ExecStart=/d "$SYSTEMD_PATH/piscsi-oled.service"
|
||||
if [ ! -z "$TOKEN" ]; then
|
||||
sudo sed -i "8 i ExecStart=$OLED_INSTALL_PATH/start.sh --rotation=$ROTATION --height=$SCREEN_HEIGHT --password=$TOKEN" "$SYSTEMD_PATH/piscsi-oled.service"
|
||||
# Make the service file readable by root only, to protect the token string
|
||||
# Make the service file readable by root only, to protect the token string
|
||||
sudo chmod 600 "$SYSTEMD_PATH/piscsi-oled.service"
|
||||
echo "Granted access to the OLED Monitor with the password that you configured for PiSCSI."
|
||||
else
|
||||
@ -1476,7 +1476,7 @@ function runChoice() {
|
||||
echo "- Install the vsftpd Webmin module"
|
||||
installWebmin
|
||||
echo "Install Webmin - Complete!"
|
||||
echo "The Webmin webapp should now be listening to port 10000 on this system"
|
||||
echo "The Webmin webapp should now be listening to port 10000 on this system"
|
||||
;;
|
||||
99)
|
||||
echo "Hidden setup mode for running the pi-gen utility"
|
||||
|
@ -148,6 +148,7 @@ class PiscsiCmds:
|
||||
scrm = []
|
||||
scmo = []
|
||||
sccd = []
|
||||
scst = []
|
||||
for dtype in mappings:
|
||||
if mappings[dtype] == proto.PbDeviceType.SCHD:
|
||||
schd.append(dtype)
|
||||
@ -157,6 +158,8 @@ class PiscsiCmds:
|
||||
scmo.append(dtype)
|
||||
elif mappings[dtype] == proto.PbDeviceType.SCCD:
|
||||
sccd.append(dtype)
|
||||
elif mappings[dtype] == proto.PbDeviceType.SCST:
|
||||
scst.append(dtype)
|
||||
|
||||
return {
|
||||
"status": result.status,
|
||||
@ -170,6 +173,7 @@ class PiscsiCmds:
|
||||
"scrm": scrm,
|
||||
"scmo": scmo,
|
||||
"sccd": sccd,
|
||||
"scst": scst,
|
||||
}
|
||||
|
||||
def get_reserved_ids(self):
|
||||
|
@ -442,5 +442,17 @@
|
||||
"file_type": null,
|
||||
"description": "For use with host systems that expect the non-standard 512 byte block size for CD-ROM drives, such as Akai samplers.",
|
||||
"url": ""
|
||||
},
|
||||
{
|
||||
"device_type": "SCST",
|
||||
"vendor": "TANDBERG",
|
||||
"product": " TDC 3600",
|
||||
"revision": null,
|
||||
"block_size": 512,
|
||||
"size": 262144000,
|
||||
"name": "250 Mb Cartige",
|
||||
"file_type": "tap",
|
||||
"description": "DC 6250 Data Cartige.",
|
||||
"url": ""
|
||||
}
|
||||
]
|
||||
|
@ -110,6 +110,10 @@
|
||||
{% if f["name"].lower().endswith(env['mo_suffixes']) %}
|
||||
<option value="{{ f["name"] }}">{{ f["name"].replace(env["image_dir"], '') }}</option>
|
||||
{% endif %}
|
||||
{% elif device.device_type == "SCST" %}
|
||||
{% if f["name"].lower().endswith(env['st_suffixes']) %}
|
||||
<option value="{{ f["name"] }}">{{ f["name"].replace(env["image_dir"], '') }}</option>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
@ -487,6 +491,13 @@
|
||||
</option>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if type == "SCST" %}
|
||||
{% for drive in drive_properties["st_conf"] | sort(attribute='name') %}
|
||||
<option value="{{ drive.name }}">
|
||||
{{ drive.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</select>
|
||||
<label for="{{ type }}_image_file_name">{{ _("Image file:") }}</label>
|
||||
<select name="file_name" id="{{ type }}_image_file_name" class="table-dropdown" {% if type not in REMOVABLE_DEVICE_TYPES %}required{% endif %}>
|
||||
|
@ -130,6 +130,7 @@ def get_env_info():
|
||||
"cd_suffixes": tuple(server_info["sccd"]),
|
||||
"rm_suffixes": tuple(server_info["scrm"]),
|
||||
"mo_suffixes": tuple(server_info["scmo"]),
|
||||
"st_suffixes": tuple(server_info["scst"]),
|
||||
"throttle_status": [
|
||||
(s[0], ReturnCodeMapper.add_msg({"return_code": s[1]})) for s in throttled_statuses
|
||||
],
|
||||
@ -252,10 +253,15 @@ def index():
|
||||
)
|
||||
+ server_info["scrm"]
|
||||
+ server_info["scmo"]
|
||||
+ server_info["scst"]
|
||||
)
|
||||
|
||||
valid_image_suffixes = (
|
||||
server_info["schd"] + server_info["scrm"] + server_info["scmo"] + server_info["sccd"]
|
||||
server_info["schd"]
|
||||
+ server_info["scrm"]
|
||||
+ server_info["scmo"]
|
||||
+ server_info["sccd"]
|
||||
+ server_info["scst"]
|
||||
)
|
||||
|
||||
return response(
|
||||
|
@ -110,6 +110,8 @@ def get_device_name(device_type):
|
||||
return _("Printer")
|
||||
if device_type == "SCHS":
|
||||
return _("Host Services")
|
||||
if device_type == "SCST":
|
||||
return _("Streamer (Tape) Drive")
|
||||
return device_type
|
||||
|
||||
|
||||
@ -143,6 +145,10 @@ def get_image_description(file_suffix):
|
||||
return _("Removable Disk Image")
|
||||
if file_suffix == "mos":
|
||||
return _("Magneto-Optical Disk Image")
|
||||
if file_suffix == "tap":
|
||||
return _("Tape Image")
|
||||
if file_suffix == "tar":
|
||||
return _("Tape Archive")
|
||||
return file_suffix
|
||||
|
||||
|
||||
@ -185,7 +191,7 @@ def format_drive_properties(drive_properties):
|
||||
cd_conf = []
|
||||
rm_conf = []
|
||||
mo_conf = []
|
||||
|
||||
st_conf = []
|
||||
for device in drive_properties:
|
||||
# Fallback for when the properties data is corrupted, to avoid crashing the web app.
|
||||
# The integration tests will catch this scenario, but relies on the web app not crashing.
|
||||
@ -205,12 +211,15 @@ def format_drive_properties(drive_properties):
|
||||
rm_conf.append(device)
|
||||
elif device["device_type"] == "SCMO":
|
||||
mo_conf.append(device)
|
||||
elif device["device_type"] == "SCST":
|
||||
st_conf.append(device)
|
||||
|
||||
return {
|
||||
"hd_conf": hd_conf,
|
||||
"cd_conf": cd_conf,
|
||||
"rm_conf": rm_conf,
|
||||
"mo_conf": mo_conf,
|
||||
"st_conf": st_conf,
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user