mirror of
https://github.com/akuker/RASCSI.git
synced 2024-06-14 14:29:33 +00:00
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
|
|||
|
};
|