mirror of https://github.com/akuker/RASCSI.git
205 lines
5.7 KiB
C++
205 lines
5.7 KiB
C++
//---------------------------------------------------------------------------
|
||
//
|
||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||
// for Raspberry Pi
|
||
//
|
||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||
// Copyright (C) 2014-2020 GIMONS
|
||
// Copyright (C) akuker
|
||
//
|
||
// Licensed under the BSD 3-Clause License.
|
||
// See LICENSE file in the project root folder.
|
||
//
|
||
// [ SASI device controller ]
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
#pragma once
|
||
|
||
#include "../config.h"
|
||
#include "os.h"
|
||
#include "scsi.h"
|
||
#include "fileio.h"
|
||
|
||
class Device;
|
||
class PrimaryDevice;
|
||
|
||
//===========================================================================
|
||
//
|
||
// SASI Controller
|
||
//
|
||
//===========================================================================
|
||
class SASIDEV
|
||
{
|
||
protected:
|
||
enum scsi_message_code : BYTE {
|
||
eMsgCodeAbort = 0x06,
|
||
eMsgCodeAbortTag = 0x0D,
|
||
eMsgCodeBusDeviceReset = 0x0C,
|
||
eMsgCodeClearQueue = 0x0E,
|
||
eMsgCodeCommandComplete = 0x00,
|
||
eMsgCodeDisconnect = 0x04,
|
||
eMsgCodeIdentify = 0x80,
|
||
eMsgCodeIgnoreWideResidue = 0x23, // (Two Bytes)
|
||
eMsgCodeInitiateRecovery = 0x0F,
|
||
eMsgCodeInitiatorDetectedError = 0x05,
|
||
eMsgCodeLinkedCommandComplete = 0x0A,
|
||
eMsgCodeLinkedCommandCompleteWithFlag = 0x0B,
|
||
eMsgCodeMessageParityError = 0x09,
|
||
eMsgCodeMessageReject = 0x07,
|
||
eMsgCodeNoOperation = 0x08,
|
||
eMsgCodeHeadOfQueueTag = 0x21,
|
||
eMsgCodeOrderedQueueTag = 0x22,
|
||
eMsgCodeSimpleQueueTag = 0x20,
|
||
eMsgCodeReleaseRecovery = 0x10,
|
||
eMsgCodeRestorePointers = 0x03,
|
||
eMsgCodeSaveDataPointer = 0x02,
|
||
eMsgCodeTerminateIOProcess = 0x11
|
||
};
|
||
|
||
private:
|
||
enum sasi_command : int {
|
||
eCmdTestUnitReady = 0x00,
|
||
eCmdRezero = 0x01,
|
||
eCmdRequestSense = 0x03,
|
||
eCmdFormat = 0x06,
|
||
eCmdReassign = 0x07,
|
||
eCmdRead6 = 0x08,
|
||
eCmdWrite6 = 0x0A,
|
||
eCmdSeek6 = 0x0B,
|
||
eCmdSetMcastAddr = 0x0D, // DaynaPort specific command
|
||
eCmdModeSelect6 = 0x15,
|
||
eCmdReserve6 = 0x16,
|
||
eCmdRelease6 = 0x17,
|
||
eCmdRead10 = 0x28,
|
||
eCmdWrite10 = 0x2A,
|
||
eCmdVerify10 = 0x2E,
|
||
eCmdVerify = 0x2F,
|
||
eCmdModeSelect10 = 0x55,
|
||
eCmdRead16 = 0x88,
|
||
eCmdWrite16 = 0x8A,
|
||
eCmdVerify16 = 0x8F,
|
||
eCmdWriteLong10 = 0x3F,
|
||
eCmdWriteLong16 = 0x9F,
|
||
eCmdInvalid = 0xC2,
|
||
eCmdSasiCmdAssign = 0x0E
|
||
};
|
||
|
||
public:
|
||
enum {
|
||
UnitMax = 32 // Maximum number of logical units
|
||
};
|
||
|
||
const int UNKNOWN_SCSI_ID = -1;
|
||
const int DEFAULT_BUFFER_SIZE = 0x1000;
|
||
// TODO Remove this duplicate
|
||
const int DAYNAPORT_BUFFER_SIZE = 0x1000000;
|
||
|
||
// For timing adjustments
|
||
enum {
|
||
min_exec_time_sasi = 100, // SASI BOOT/FORMAT 30:NG 35:OK
|
||
min_exec_time_scsi = 50
|
||
};
|
||
|
||
// Internal data definition
|
||
typedef struct {
|
||
// General
|
||
BUS::phase_t phase; // Transition phase
|
||
int m_scsi_id; // Controller ID (0-7)
|
||
BUS *bus; // Bus
|
||
|
||
// commands
|
||
DWORD cmd[16]; // Command data
|
||
DWORD status; // Status data
|
||
DWORD message; // Message data
|
||
|
||
// Run
|
||
DWORD execstart; // Execution start time
|
||
|
||
// Transfer
|
||
BYTE *buffer; // Transfer data buffer
|
||
int bufsize; // Transfer data buffer size
|
||
uint32_t blocks; // Number of transfer block
|
||
DWORD next; // Next record
|
||
DWORD offset; // Transfer offset
|
||
DWORD length; // Transfer remaining length
|
||
|
||
// Logical unit
|
||
PrimaryDevice *unit[UnitMax];
|
||
|
||
// The current device
|
||
PrimaryDevice *device;
|
||
|
||
// The LUN from the IDENTIFY message
|
||
int lun;
|
||
} ctrl_t;
|
||
|
||
public:
|
||
// Basic Functions
|
||
SASIDEV();
|
||
virtual ~SASIDEV(); // Destructor
|
||
virtual void Reset(); // Device Reset
|
||
|
||
// External API
|
||
virtual BUS::phase_t Process(int); // Run
|
||
|
||
// Connect
|
||
void Connect(int id, BUS *sbus); // Controller connection
|
||
Device* GetUnit(int no); // Get logical unit
|
||
void SetUnit(int no, PrimaryDevice *dev); // Logical unit setting
|
||
bool HasUnit(); // Has a valid logical unit
|
||
|
||
// Other
|
||
BUS::phase_t GetPhase() {return ctrl.phase;} // Get the phase
|
||
|
||
int GetSCSIID() {return ctrl.m_scsi_id;} // Get the ID
|
||
ctrl_t* GetCtrl() { return &ctrl; } // Get the internal information address
|
||
virtual bool IsSASI() const { return true; } // SASI Check
|
||
virtual bool IsSCSI() const { return false; } // SCSI check
|
||
|
||
public:
|
||
void DataIn(); // Data in phase
|
||
void Status(); // Status phase
|
||
void MsgIn(); // Message in phase
|
||
void DataOut(); // Data out phase
|
||
|
||
// Get LUN based on IDENTIFY message, with LUN from the CDB as fallback
|
||
int GetEffectiveLun() const;
|
||
|
||
virtual void Error(ERROR_CODES::sense_key sense_key = ERROR_CODES::sense_key::NO_SENSE,
|
||
ERROR_CODES::asc = ERROR_CODES::asc::NO_ADDITIONAL_SENSE_INFORMATION,
|
||
ERROR_CODES::status = ERROR_CODES::status::CHECK_CONDITION); // Common error handling
|
||
|
||
protected:
|
||
// Phase processing
|
||
virtual void BusFree(); // Bus free phase
|
||
virtual void Selection(); // Selection phase
|
||
virtual void Command(); // Command phase
|
||
virtual void Execute(); // Execution phase
|
||
|
||
// Commands
|
||
void CmdTestUnitReady(); // TEST UNIT READY command
|
||
void CmdRezero(); // REZERO UNIT command
|
||
void CmdRequestSense(); // REQUEST SENSE command
|
||
void CmdFormat(); // FORMAT command
|
||
void CmdReassignBlocks(); // REASSIGN BLOCKS command
|
||
void CmdReserveUnit(); // RESERVE UNIT command
|
||
void CmdReleaseUnit(); // RELEASE UNIT command
|
||
void CmdRead6(); // READ(6) command
|
||
void CmdWrite6(); // WRITE(6) command
|
||
void CmdSeek6(); // SEEK(6) command
|
||
void CmdAssign(); // ASSIGN command
|
||
void CmdSpecify(); // SPECIFY command
|
||
|
||
// Data transfer
|
||
virtual void Send(); // Send data
|
||
virtual void Receive(); // Receive data
|
||
|
||
bool XferIn(BYTE* buf); // Data transfer IN
|
||
virtual bool XferOut(bool cont); // Data transfer OUT
|
||
|
||
// Special operations
|
||
void FlushUnit(); // Flush the logical unit
|
||
|
||
ctrl_t ctrl; // Internal data
|
||
};
|