mirror of
https://github.com/akuker/RASCSI.git
synced 2024-07-04 14:29:33 +00:00
b3862c4726
* Replaced DWORD ID by string * Signature update * Removed unused code * Added getters for all device types * Renaming * Removed unused code * Renaming * Updated more ID checks * Removed unused code * Use IsSCSI()
1572 lines
36 KiB
C++
1572 lines
36 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 ]
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
#include "controllers/sasidev_ctrl.h"
|
||
#include "filepath.h"
|
||
#include "gpiobus.h"
|
||
#include "devices/scsi_host_bridge.h"
|
||
#include "devices/scsi_daynaport.h"
|
||
#include "exceptions.h"
|
||
|
||
//===========================================================================
|
||
//
|
||
// SASI Device
|
||
//
|
||
//===========================================================================
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Constructor
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
SASIDEV::SASIDEV()
|
||
{
|
||
int i;
|
||
|
||
// Work initialization
|
||
ctrl.phase = BUS::busfree;
|
||
ctrl.m_scsi_id = UNKNOWN_SCSI_ID;
|
||
ctrl.bus = NULL;
|
||
memset(ctrl.cmd, 0x00, sizeof(ctrl.cmd));
|
||
ctrl.status = 0x00;
|
||
ctrl.message = 0x00;
|
||
ctrl.execstart = 0;
|
||
// The initial buffer size will default to either the default buffer size OR
|
||
// the size of an Ethernet message, whichever is larger.
|
||
ctrl.bufsize = std::max(DEFAULT_BUFFER_SIZE, ETH_FRAME_LEN + 16 + ETH_FCS_LEN);
|
||
ctrl.buffer = (BYTE *)malloc(ctrl.bufsize);
|
||
memset(ctrl.buffer, 0x00, ctrl.bufsize);
|
||
ctrl.blocks = 0;
|
||
ctrl.next = 0;
|
||
ctrl.offset = 0;
|
||
ctrl.length = 0;
|
||
|
||
// Logical unit initialization
|
||
for (i = 0; i < UnitMax; i++) {
|
||
ctrl.unit[i] = NULL;
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Destructor
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
SASIDEV::~SASIDEV()
|
||
{
|
||
// Free the buffer
|
||
if (ctrl.buffer) {
|
||
free(ctrl.buffer);
|
||
ctrl.buffer = NULL;
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Device reset
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::Reset()
|
||
{
|
||
// Work initialization
|
||
memset(ctrl.cmd, 0x00, sizeof(ctrl.cmd));
|
||
ctrl.phase = BUS::busfree;
|
||
ctrl.status = 0x00;
|
||
ctrl.message = 0x00;
|
||
ctrl.execstart = 0;
|
||
memset(ctrl.buffer, 0x00, ctrl.bufsize);
|
||
ctrl.blocks = 0;
|
||
ctrl.next = 0;
|
||
ctrl.offset = 0;
|
||
ctrl.length = 0;
|
||
|
||
// Unit initialization
|
||
for (int i = 0; i < UnitMax; i++) {
|
||
if (ctrl.unit[i]) {
|
||
ctrl.unit[i]->Reset();
|
||
}
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Connect the controller
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::Connect(int id, BUS *bus)
|
||
{
|
||
ctrl.m_scsi_id = id;
|
||
ctrl.bus = bus;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Get the logical unit
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
Disk* FASTCALL SASIDEV::GetUnit(int no)
|
||
{
|
||
ASSERT(no < UnitMax);
|
||
|
||
return ctrl.unit[no];
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Set the logical unit
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::SetUnit(int no, Disk *dev)
|
||
{
|
||
ASSERT(no < UnitMax);
|
||
|
||
ctrl.unit[no] = dev;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Check to see if this has a valid logical unit
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL SASIDEV::HasUnit()
|
||
{
|
||
for (int i = 0; i < UnitMax; i++) {
|
||
if (ctrl.unit[i]) {
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Get internal data
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::GetCTRL(ctrl_t *buffer)
|
||
{
|
||
ASSERT(buffer);
|
||
|
||
// reference the internal structure
|
||
*buffer = ctrl;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Get a busy unit
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
Disk* FASTCALL SASIDEV::GetBusyUnit()
|
||
{
|
||
// Logical Unit
|
||
DWORD lun = (ctrl.cmd[1] >> 5) & 0x07;
|
||
return ctrl.unit[lun];
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Run
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BUS::phase_t FASTCALL SASIDEV::Process()
|
||
{
|
||
// Do nothing if not connected
|
||
if (ctrl.m_scsi_id < 0 || ctrl.bus == NULL) {
|
||
return ctrl.phase;
|
||
}
|
||
|
||
// Get bus information
|
||
((GPIOBUS*)ctrl.bus)->Aquire();
|
||
|
||
// For the monitor tool, we shouldn't need to reset. We're just logging information
|
||
// Reset
|
||
if (ctrl.bus->GetRST()) {
|
||
LOGINFO("RESET signal received");
|
||
|
||
// Reset the controller
|
||
Reset();
|
||
|
||
// Reset the bus
|
||
ctrl.bus->Reset();
|
||
return ctrl.phase;
|
||
}
|
||
|
||
// Phase processing
|
||
switch (ctrl.phase) {
|
||
// Bus free
|
||
case BUS::busfree:
|
||
BusFree();
|
||
break;
|
||
|
||
// Selection
|
||
case BUS::selection:
|
||
Selection();
|
||
break;
|
||
|
||
// Data out (MCI=000)
|
||
case BUS::dataout:
|
||
DataOut();
|
||
break;
|
||
|
||
// Data in (MCI=001)
|
||
case BUS::datain:
|
||
DataIn();
|
||
break;
|
||
|
||
// Command (MCI=010)
|
||
case BUS::command:
|
||
Command();
|
||
break;
|
||
|
||
// Status (MCI=011)
|
||
case BUS::status:
|
||
Status();
|
||
break;
|
||
|
||
// Msg in (MCI=111)
|
||
case BUS::msgin:
|
||
MsgIn();
|
||
break;
|
||
|
||
// Other
|
||
default:
|
||
ASSERT(FALSE);
|
||
break;
|
||
}
|
||
|
||
return ctrl.phase;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Bus free phase
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::BusFree()
|
||
{
|
||
// Phase change
|
||
if (ctrl.phase != BUS::busfree) {
|
||
LOGINFO("Bus free phase");
|
||
|
||
// Phase Setting
|
||
ctrl.phase = BUS::busfree;
|
||
|
||
// Set Signal lines
|
||
ctrl.bus->SetREQ(FALSE);
|
||
ctrl.bus->SetMSG(FALSE);
|
||
ctrl.bus->SetCD(FALSE);
|
||
ctrl.bus->SetIO(FALSE);
|
||
ctrl.bus->SetBSY(FALSE);
|
||
|
||
// Initialize status and message
|
||
ctrl.status = 0x00;
|
||
ctrl.message = 0x00;
|
||
return;
|
||
}
|
||
|
||
// Move to selection phase
|
||
if (ctrl.bus->GetSEL() && !ctrl.bus->GetBSY()) {
|
||
Selection();
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Selection phase
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::Selection()
|
||
{
|
||
// Phase change
|
||
if (ctrl.phase != BUS::selection) {
|
||
// Invalid if IDs do not match
|
||
DWORD id = 1 << ctrl.m_scsi_id;
|
||
if ((ctrl.bus->GetDAT() & id) == 0) {
|
||
return;
|
||
}
|
||
|
||
// Return if there is no unit
|
||
if (!HasUnit()) {
|
||
return;
|
||
}
|
||
|
||
LOGTRACE("%s Selection Phase ID=%d (with device)", __PRETTY_FUNCTION__, (int)ctrl.m_scsi_id);
|
||
|
||
// Phase change
|
||
ctrl.phase = BUS::selection;
|
||
|
||
// Raiase BSY and respond
|
||
ctrl.bus->SetBSY(TRUE);
|
||
return;
|
||
}
|
||
|
||
// Command phase shifts when selection is completed
|
||
if (!ctrl.bus->GetSEL() && ctrl.bus->GetBSY()) {
|
||
Command();
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Command phase
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::Command()
|
||
{
|
||
int count;
|
||
int i;
|
||
|
||
// Phase change
|
||
if (ctrl.phase != BUS::command) {
|
||
LOGTRACE("%s Command Phase", __PRETTY_FUNCTION__);
|
||
|
||
// Phase Setting
|
||
ctrl.phase = BUS::command;
|
||
|
||
// Signal line operated by the target
|
||
ctrl.bus->SetMSG(FALSE);
|
||
ctrl.bus->SetCD(TRUE);
|
||
ctrl.bus->SetIO(FALSE);
|
||
|
||
// Data transfer is 6 bytes x 1 block
|
||
ctrl.offset = 0;
|
||
ctrl.length = 6;
|
||
ctrl.blocks = 1;
|
||
|
||
// Command reception handshake (10 bytes are automatically received at the first command)
|
||
count = ctrl.bus->CommandHandShake(ctrl.buffer);
|
||
|
||
// If no byte can be received move to the status phase
|
||
if (count == 0) {
|
||
Error();
|
||
return;
|
||
}
|
||
|
||
// Check 10-byte CDB
|
||
if (ctrl.buffer[0] >= 0x20 && ctrl.buffer[0] <= 0x7D) {
|
||
ctrl.length = 10;
|
||
}
|
||
|
||
// If not able to receive all, move to the status phase
|
||
if (count != (int)ctrl.length) {
|
||
Error();
|
||
return;
|
||
}
|
||
|
||
// Command data transfer
|
||
for (i = 0; i < (int)ctrl.length; i++) {
|
||
ctrl.cmd[i] = (DWORD)ctrl.buffer[i];
|
||
}
|
||
|
||
// Clear length and block
|
||
ctrl.length = 0;
|
||
ctrl.blocks = 0;
|
||
|
||
// Execution Phase
|
||
try {
|
||
Execute();
|
||
}
|
||
catch (lunexception& e) {
|
||
LOGINFO("%s Invalid LUN %d", __PRETTY_FUNCTION__, (int)e.getlun());
|
||
|
||
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::INVALID_LUN);
|
||
}
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Execution Phase
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::Execute()
|
||
{
|
||
LOGTRACE("%s Execution Phase Command %02X", __PRETTY_FUNCTION__, (WORD)ctrl.cmd[0]);
|
||
|
||
// Phase Setting
|
||
ctrl.phase = BUS::execute;
|
||
|
||
// Initialization for data transfer
|
||
ctrl.offset = 0;
|
||
ctrl.blocks = 1;
|
||
ctrl.execstart = SysTimer::GetTimerLow();
|
||
|
||
// Discard pending sense data from the previous command if the current command is not REQUEST SENSE
|
||
if ((SASIDEV::scsi_command)ctrl.cmd[0] != eCmdRequestSense) {
|
||
ctrl.sense_key = 0;
|
||
ctrl.asc = 0;
|
||
}
|
||
|
||
// Process by command
|
||
switch ((SASIDEV::scsi_command)ctrl.cmd[0]) {
|
||
// TEST UNIT READY
|
||
case SASIDEV::eCmdTestUnitReady:
|
||
CmdTestUnitReady();
|
||
return;
|
||
|
||
// REZERO UNIT
|
||
case SASIDEV::eCmdRezero:
|
||
CmdRezero();
|
||
return;
|
||
|
||
// REQUEST SENSE
|
||
case SASIDEV::eCmdRequestSense:
|
||
CmdRequestSense();
|
||
return;
|
||
|
||
// FORMAT UNIT
|
||
// This doesn't exist in the SCSI Spec, but was in the original RaSCSI code.
|
||
// leaving it here for now....
|
||
case SASIDEV::eCmdFormat:
|
||
CmdFormat();
|
||
return;
|
||
|
||
// REASSIGN BLOCKS
|
||
case SASIDEV::eCmdReassign:
|
||
CmdReassign();
|
||
return;
|
||
|
||
// READ(6)
|
||
case SASIDEV::eCmdRead6:
|
||
CmdRead6();
|
||
return;
|
||
|
||
// WRITE(6)
|
||
case SASIDEV::eCmdWrite6:
|
||
CmdWrite6();
|
||
return;
|
||
|
||
// SEEK(6)
|
||
case SASIDEV::eCmdSeek6:
|
||
CmdSeek6();
|
||
return;
|
||
|
||
// ASSIGN(SASIのみ)
|
||
// This doesn't exist in the SCSI Spec, but was in the original RaSCSI code.
|
||
// leaving it here for now....
|
||
case SASIDEV::eCmdSasiCmdAssign:
|
||
CmdAssign();
|
||
return;
|
||
|
||
// RESERVE UNIT(16)
|
||
case SASIDEV::eCmdReserve6:
|
||
CmdReserveUnit();
|
||
return;
|
||
|
||
// RELEASE UNIT(17)
|
||
case eCmdRelease6:
|
||
CmdReleaseUnit();
|
||
return;
|
||
|
||
// SPECIFY(SASIのみ)
|
||
// This doesn't exist in the SCSI Spec, but was in the original RaSCSI code.
|
||
// leaving it here for now....
|
||
case SASIDEV::eCmdInvalid:
|
||
CmdSpecify();
|
||
return;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
// Unsupported command
|
||
LOGWARN("%s Unsupported command $%02X", __PRETTY_FUNCTION__, (WORD)ctrl.cmd[0]);
|
||
CmdInvalid();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Status phase
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::Status()
|
||
{
|
||
DWORD min_exec_time;
|
||
DWORD time;
|
||
|
||
// Phase change
|
||
if (ctrl.phase != BUS::status) {
|
||
// Minimum execution time
|
||
if (ctrl.execstart > 0) {
|
||
min_exec_time = IsSASI() ? min_exec_time_sasi : min_exec_time_scsi;
|
||
time = SysTimer::GetTimerLow() - ctrl.execstart;
|
||
if (time < min_exec_time) {
|
||
SysTimer::SleepUsec(min_exec_time - time);
|
||
}
|
||
ctrl.execstart = 0;
|
||
} else {
|
||
SysTimer::SleepUsec(5);
|
||
}
|
||
|
||
// Phase Setting
|
||
ctrl.phase = BUS::status;
|
||
|
||
// Signal line operated by the target
|
||
ctrl.bus->SetMSG(FALSE);
|
||
ctrl.bus->SetCD(TRUE);
|
||
ctrl.bus->SetIO(TRUE);
|
||
|
||
// Data transfer is 1 byte x 1 block
|
||
ctrl.offset = 0;
|
||
ctrl.length = 1;
|
||
ctrl.blocks = 1;
|
||
ctrl.buffer[0] = (BYTE)ctrl.status;
|
||
|
||
LOGTRACE( "%s Status Phase $%02X",__PRETTY_FUNCTION__, (unsigned int)ctrl.status);
|
||
|
||
return;
|
||
}
|
||
|
||
// Send
|
||
Send();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Message in phase
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::MsgIn()
|
||
{
|
||
// Phase change
|
||
if (ctrl.phase != BUS::msgin) {
|
||
LOGTRACE("%s Starting Message in phase", __PRETTY_FUNCTION__);
|
||
|
||
// Phase Setting
|
||
ctrl.phase = BUS::msgin;
|
||
|
||
// Signal line operated by the target
|
||
ctrl.bus->SetMSG(TRUE);
|
||
ctrl.bus->SetCD(TRUE);
|
||
ctrl.bus->SetIO(TRUE);
|
||
|
||
// length, blocks are already set
|
||
ASSERT(ctrl.length > 0);
|
||
ASSERT(ctrl.blocks > 0);
|
||
ctrl.offset = 0;
|
||
return;
|
||
}
|
||
|
||
//Send
|
||
LOGTRACE("%s Transitioning to Send()", __PRETTY_FUNCTION__);
|
||
Send();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Data-in Phase
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::DataIn()
|
||
{
|
||
DWORD min_exec_time;
|
||
DWORD time;
|
||
|
||
ASSERT(ctrl.length >= 0);
|
||
|
||
// Phase change
|
||
if (ctrl.phase != BUS::datain) {
|
||
// Minimum execution time
|
||
if (ctrl.execstart > 0) {
|
||
min_exec_time = IsSASI() ? min_exec_time_sasi : min_exec_time_scsi;
|
||
time = SysTimer::GetTimerLow() - ctrl.execstart;
|
||
if (time < min_exec_time) {
|
||
SysTimer::SleepUsec(min_exec_time - time);
|
||
}
|
||
ctrl.execstart = 0;
|
||
}
|
||
|
||
// If the length is 0, go to the status phase
|
||
if (ctrl.length == 0) {
|
||
Status();
|
||
return;
|
||
}
|
||
|
||
LOGTRACE("%s Going into Data-in Phase", __PRETTY_FUNCTION__);
|
||
// Phase Setting
|
||
ctrl.phase = BUS::datain;
|
||
|
||
// Signal line operated by the target
|
||
ctrl.bus->SetMSG(FALSE);
|
||
ctrl.bus->SetCD(FALSE);
|
||
ctrl.bus->SetIO(TRUE);
|
||
|
||
// length, blocks are already set
|
||
ASSERT(ctrl.length > 0);
|
||
ASSERT(ctrl.blocks > 0);
|
||
ctrl.offset = 0;
|
||
|
||
return;
|
||
}
|
||
|
||
// Send
|
||
Send();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Data out phase
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::DataOut()
|
||
{
|
||
DWORD min_exec_time;
|
||
DWORD time;
|
||
|
||
ASSERT(ctrl.length >= 0);
|
||
|
||
// Phase change
|
||
if (ctrl.phase != BUS::dataout) {
|
||
// Minimum execution time
|
||
if (ctrl.execstart > 0) {
|
||
min_exec_time = IsSASI() ? min_exec_time_sasi : min_exec_time_scsi;
|
||
time = SysTimer::GetTimerLow() - ctrl.execstart;
|
||
if (time < min_exec_time) {
|
||
SysTimer::SleepUsec(min_exec_time - time);
|
||
}
|
||
ctrl.execstart = 0;
|
||
}
|
||
|
||
// If the length is 0, go to the status phase
|
||
if (ctrl.length == 0) {
|
||
Status();
|
||
return;
|
||
}
|
||
|
||
LOGTRACE("%s Data out phase", __PRETTY_FUNCTION__);
|
||
|
||
// Phase Setting
|
||
ctrl.phase = BUS::dataout;
|
||
|
||
// Signal line operated by the target
|
||
ctrl.bus->SetMSG(FALSE);
|
||
ctrl.bus->SetCD(FALSE);
|
||
ctrl.bus->SetIO(FALSE);
|
||
|
||
// length, blocks are already calculated
|
||
ASSERT(ctrl.length > 0);
|
||
ASSERT(ctrl.blocks > 0);
|
||
ctrl.offset = 0;
|
||
return;
|
||
}
|
||
|
||
// Receive
|
||
Receive();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Error
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::Error(ERROR_CODES::sense_key sense_key, ERROR_CODES::asc asc)
|
||
{
|
||
// Get bus information
|
||
((GPIOBUS*)ctrl.bus)->Aquire();
|
||
|
||
// Reset check
|
||
if (ctrl.bus->GetRST()) {
|
||
// Reset the controller
|
||
Reset();
|
||
|
||
// Reset the bus
|
||
ctrl.bus->Reset();
|
||
return;
|
||
}
|
||
|
||
// Bus free for status phase and message in phase
|
||
if (ctrl.phase == BUS::status || ctrl.phase == BUS::msgin) {
|
||
BusFree();
|
||
return;
|
||
}
|
||
|
||
LOGTRACE("%s Sense Key and ASC for subsequent REQUEST SENSE: $%02X, $%02X", __PRETTY_FUNCTION__, sense_key, asc);
|
||
|
||
// Set Sense Key and ASC for a subsequent REQUEST SENSE
|
||
ctrl.sense_key = sense_key;
|
||
ctrl.asc = asc;
|
||
|
||
// Logical Unit
|
||
DWORD lun = (ctrl.cmd[1] >> 5) & 0x07;
|
||
|
||
// Set status and message(CHECK CONDITION)
|
||
ctrl.status = (lun << 5) | 0x02;
|
||
|
||
#if defined(DISK_LOG)
|
||
LOGWARN("Error occured (going to status phase)");
|
||
#endif // DISK_LOG
|
||
|
||
// status phase
|
||
Status();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// TEST UNIT READY
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::CmdTestUnitReady()
|
||
{
|
||
LOGTRACE("%s TEST UNIT READY Command ", __PRETTY_FUNCTION__);
|
||
|
||
DWORD lun = GetLun();
|
||
|
||
// Command processing on drive
|
||
BOOL status = ctrl.unit[lun]->TestUnitReady(ctrl.cmd);
|
||
if (!status) {
|
||
// Failure (Error)
|
||
Error();
|
||
return;
|
||
}
|
||
|
||
// status phase
|
||
Status();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// REZERO UNIT
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::CmdRezero()
|
||
{
|
||
LOGTRACE( "%s REZERO UNIT Command ", __PRETTY_FUNCTION__);
|
||
|
||
DWORD lun = GetLun();
|
||
|
||
// Command processing on drive
|
||
BOOL status = ctrl.unit[lun]->Rezero(ctrl.cmd);
|
||
if (!status) {
|
||
// Failure (Error)
|
||
Error();
|
||
return;
|
||
}
|
||
|
||
// status phase
|
||
Status();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// REQUEST SENSE
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::CmdRequestSense()
|
||
{
|
||
LOGTRACE( "%s REQUEST SENSE Command ", __PRETTY_FUNCTION__);
|
||
|
||
DWORD lun;
|
||
try {
|
||
lun = GetLun();
|
||
}
|
||
catch(const lunexception& e) {
|
||
LOGINFO("%s Non-existing LUN %d", __PRETTY_FUNCTION__, (int)e.getlun());
|
||
|
||
// Note: According to the SCSI specs the LUN handling for REQUEST SENSE is special.
|
||
// 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.
|
||
|
||
// LUN 0 can be assumed to be present (required to call RequestSense() below)
|
||
lun = 0;
|
||
|
||
ctrl.sense_key = ERROR_CODES::sense_key::ILLEGAL_REQUEST;
|
||
ctrl.asc = ERROR_CODES::asc::INVALID_LUN;
|
||
}
|
||
|
||
ctrl.length = ctrl.unit[lun]->RequestSense(ctrl.cmd, ctrl.buffer);
|
||
ASSERT(ctrl.length > 0);
|
||
|
||
LOGTRACE("%s Sense Key $%02X, ASC $%02X",__PRETTY_FUNCTION__, ctrl.sense_key, ctrl.asc);
|
||
|
||
// REQUEST SENSE returns the sense data set by the previous (failed) command
|
||
ctrl.buffer[2] = ctrl.sense_key;
|
||
ctrl.buffer[12] = ctrl.asc;
|
||
|
||
// The sense data are only valid once
|
||
ctrl.sense_key = 0;
|
||
ctrl.asc = 0;
|
||
|
||
// Read phase
|
||
DataIn();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// FORMAT UNIT
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::CmdFormat()
|
||
{
|
||
LOGTRACE( "%s FORMAT UNIT Command ", __PRETTY_FUNCTION__);
|
||
|
||
DWORD lun = GetLun();
|
||
|
||
// Command processing on drive
|
||
BOOL status = ctrl.unit[lun]->Format(ctrl.cmd);
|
||
if (!status) {
|
||
// Failure (Error)
|
||
Error();
|
||
return;
|
||
}
|
||
|
||
// status phase
|
||
Status();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// REASSIGN BLOCKS
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::CmdReassign()
|
||
{
|
||
LOGTRACE("%s REASSIGN BLOCKS Command ", __PRETTY_FUNCTION__);
|
||
|
||
DWORD lun = GetLun();
|
||
|
||
// Command processing on drive
|
||
BOOL status = ctrl.unit[lun]->Reassign(ctrl.cmd);
|
||
if (!status) {
|
||
// Failure (Error)
|
||
Error();
|
||
return;
|
||
}
|
||
|
||
// status phase
|
||
Status();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// RESERVE UNIT(16)
|
||
//
|
||
// The reserve/release commands are only used in multi-initiator
|
||
// environments. RaSCSI doesn't support this use case. However, some old
|
||
// versions of Solaris will issue the reserve/release commands. We will
|
||
// just respond with an OK status.
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::CmdReserveUnit()
|
||
{
|
||
LOGTRACE( "%s Reserve(6) Command", __PRETTY_FUNCTION__);
|
||
|
||
// status phase
|
||
Status();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// RELEASE UNIT(17)
|
||
//
|
||
// The reserve/release commands are only used in multi-initiator
|
||
// environments. RaSCSI doesn't support this use case. However, some old
|
||
// versions of Solaris will issue the reserve/release commands. We will
|
||
// just respond with an OK status.
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::CmdReleaseUnit()
|
||
{
|
||
LOGTRACE( "%s Release(6) Command", __PRETTY_FUNCTION__);
|
||
|
||
// status phase
|
||
Status();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// READ(6)
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::CmdRead6()
|
||
{
|
||
DWORD lun = GetLun();
|
||
|
||
// Get record number and block number
|
||
DWORD record = ctrl.cmd[1] & 0x1f;
|
||
record <<= 8;
|
||
record |= ctrl.cmd[2];
|
||
record <<= 8;
|
||
record |= ctrl.cmd[3];
|
||
ctrl.blocks = ctrl.cmd[4];
|
||
if (ctrl.blocks == 0) {
|
||
ctrl.blocks = 0x100;
|
||
}
|
||
|
||
if(ctrl.unit[lun]->IsDaynaPort()){
|
||
// The DaynaPort only wants one block.
|
||
// ctrl.cmd[4] and ctrl.cmd[5] are used to specify the maximum buffer size for the DaynaPort
|
||
ctrl.blocks=1;
|
||
}
|
||
|
||
LOGTRACE("%s READ(6) command record=%06X blocks=%d ID %s", __PRETTY_FUNCTION__, (unsigned int)record, (int)ctrl.blocks, ctrl.unit[lun]->GetID().c_str());
|
||
|
||
// Command processing on drive
|
||
ctrl.length = ctrl.unit[lun]->Read(ctrl.cmd, ctrl.buffer, record);
|
||
LOGTRACE("%s ctrl.length is %d", __PRETTY_FUNCTION__, (int)ctrl.length);
|
||
|
||
// The DaynaPort will respond a status of 0x02 when a read of size 1 occurs.
|
||
if (ctrl.length <= 0 && !ctrl.unit[lun]->IsDaynaPort()) {
|
||
// Failure (Error)
|
||
Error();
|
||
return;
|
||
}
|
||
|
||
// Set next block
|
||
ctrl.next = record + 1;
|
||
|
||
// Read phase
|
||
DataIn();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// This Send Message command is used by the DaynaPort SCSI/Link
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::DaynaPortWrite()
|
||
{
|
||
DWORD lun = GetLun();
|
||
|
||
// Error if not a host bridge
|
||
if (!ctrl.unit[lun]->IsDaynaPort()) {
|
||
LOGERROR("Received DaynaPortWrite for a non-DaynaPort device");
|
||
Error();
|
||
return;
|
||
}
|
||
|
||
// Reallocate buffer (because it is not transfer for each block)
|
||
if (ctrl.bufsize < DAYNAPORT_BUFFER_SIZE) {
|
||
free(ctrl.buffer);
|
||
ctrl.bufsize = DAYNAPORT_BUFFER_SIZE;
|
||
ctrl.buffer = (BYTE *)malloc(ctrl.bufsize);
|
||
}
|
||
|
||
DWORD data_format = ctrl.cmd[5];
|
||
|
||
if(data_format == 0x00){
|
||
ctrl.length = (WORD)ctrl.cmd[4] + ((WORD)ctrl.cmd[3] << 8);
|
||
}
|
||
else if (data_format == 0x80){
|
||
ctrl.length = (WORD)ctrl.cmd[4] + ((WORD)ctrl.cmd[3] << 8) + 8;
|
||
}
|
||
else
|
||
{
|
||
LOGWARN("%s Unknown data format %02X", __PRETTY_FUNCTION__, (unsigned int)data_format);
|
||
}
|
||
LOGTRACE("%s length: %04X (%d) format: %02X", __PRETTY_FUNCTION__, (unsigned int)ctrl.length, (int)ctrl.length, (unsigned int)data_format);
|
||
|
||
if (ctrl.length <= 0) {
|
||
// Failure (Error)
|
||
Error();
|
||
return;
|
||
}
|
||
|
||
// Set next block
|
||
ctrl.blocks = 1;
|
||
ctrl.next = 1;
|
||
|
||
// Light phase
|
||
DataOut();
|
||
}
|
||
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// WRITE(6)
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::CmdWrite6()
|
||
{
|
||
DWORD lun = GetLun();
|
||
|
||
// Special receive function for the DaynaPort
|
||
if (ctrl.unit[lun]->IsDaynaPort()){
|
||
DaynaPortWrite();
|
||
return;
|
||
}
|
||
|
||
// Get record number and block number
|
||
DWORD record = ctrl.cmd[1] & 0x1f;
|
||
record <<= 8;
|
||
record |= ctrl.cmd[2];
|
||
record <<= 8;
|
||
record |= ctrl.cmd[3];
|
||
ctrl.blocks = ctrl.cmd[4];
|
||
if (ctrl.blocks == 0) {
|
||
ctrl.blocks = 0x100;
|
||
}
|
||
|
||
LOGTRACE("%s WRITE(6) command record=%06X blocks=%d", __PRETTY_FUNCTION__, (WORD)record, (WORD)ctrl.blocks);
|
||
|
||
// Command processing on drive
|
||
ctrl.length = ctrl.unit[lun]->WriteCheck(record);
|
||
if (ctrl.length <= 0) {
|
||
LOGDEBUG("WriteCheck failed");
|
||
// Failure (Error)
|
||
Error();
|
||
return;
|
||
}
|
||
|
||
// Set next block
|
||
ctrl.next = record + 1;
|
||
|
||
// Write phase
|
||
DataOut();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// SEEK(6)
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::CmdSeek6()
|
||
{
|
||
LOGTRACE("%s SEEK(6) Command ", __PRETTY_FUNCTION__);
|
||
|
||
DWORD lun = GetLun();
|
||
|
||
// Command processing on drive
|
||
BOOL status = ctrl.unit[lun]->Seek(ctrl.cmd);
|
||
if (!status) {
|
||
// Failure (Error)
|
||
Error();
|
||
return;
|
||
}
|
||
|
||
// status phase
|
||
Status();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// ASSIGN
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::CmdAssign()
|
||
{
|
||
LOGTRACE("%s ASSIGN Command ", __PRETTY_FUNCTION__);
|
||
|
||
DWORD lun = GetLun();
|
||
|
||
// Command processing on drive
|
||
BOOL status = ctrl.unit[lun]->Assign(ctrl.cmd);
|
||
if (!status) {
|
||
// Failure (Error)
|
||
Error();
|
||
return;
|
||
}
|
||
|
||
// Request 4 bytes of data
|
||
ctrl.length = 4;
|
||
|
||
// Write phase
|
||
DataOut();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// SPECIFY
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::CmdSpecify()
|
||
{
|
||
LOGTRACE("%s SPECIFY Command ", __PRETTY_FUNCTION__);
|
||
|
||
DWORD lun = GetLun();
|
||
|
||
// Command processing on drive
|
||
BOOL status = ctrl.unit[lun]->Assign(ctrl.cmd);
|
||
if (!status) {
|
||
// Failure (Error)
|
||
Error();
|
||
return;
|
||
}
|
||
|
||
// Request 10 bytes of data
|
||
ctrl.length = 10;
|
||
|
||
// Write phase
|
||
DataOut();
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Unsupported command
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::CmdInvalid()
|
||
{
|
||
LOGWARN("%s Command not supported", __PRETTY_FUNCTION__);
|
||
|
||
Error(ERROR_CODES::sense_key::ILLEGAL_REQUEST, ERROR_CODES::asc::INVALID_COMMAND_OPERATION_CODE);
|
||
}
|
||
|
||
//===========================================================================
|
||
//
|
||
// Data transfer
|
||
//
|
||
//===========================================================================
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Data transmission
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::Send()
|
||
{
|
||
int len;
|
||
BOOL result;
|
||
|
||
ASSERT(!ctrl.bus->GetREQ());
|
||
ASSERT(ctrl.bus->GetIO());
|
||
|
||
// Check that the length isn't 0
|
||
if (ctrl.length != 0) {
|
||
len = ctrl.bus->SendHandShake(
|
||
&ctrl.buffer[ctrl.offset], ctrl.length, BUS::SEND_NO_DELAY);
|
||
|
||
// If you can not send it all, move on to the status phase
|
||
if (len != (int)ctrl.length) {
|
||
LOGERROR("%s ctrl.length (%d) did not match the amount of data sent (%d)",__PRETTY_FUNCTION__, (int)ctrl.length, len);
|
||
Error();
|
||
return;
|
||
}
|
||
|
||
// Offset and Length
|
||
ctrl.offset += ctrl.length;
|
||
ctrl.length = 0;
|
||
return;
|
||
}
|
||
else{
|
||
LOGINFO("%s ctrl.length was 0", __PRETTY_FUNCTION__);
|
||
}
|
||
|
||
// Remove block and initialize the result
|
||
ctrl.blocks--;
|
||
result = TRUE;
|
||
|
||
// Process after data collection (read/data-in only)
|
||
if (ctrl.phase == BUS::datain) {
|
||
if (ctrl.blocks != 0) {
|
||
// Set next buffer (set offset, length)
|
||
result = XferIn(ctrl.buffer);
|
||
LOGTRACE("%s xfer in: %d",__PRETTY_FUNCTION__, result);
|
||
LOGTRACE("%s processing after data collection", __PRETTY_FUNCTION__);
|
||
}
|
||
}
|
||
|
||
// If result FALSE, move to the status phase
|
||
if (!result) {
|
||
LOGERROR("%s Send result was false", __PRETTY_FUNCTION__);
|
||
Error();
|
||
return;
|
||
}
|
||
|
||
// Continue sending if block != 0
|
||
if (ctrl.blocks != 0){
|
||
ASSERT(ctrl.length > 0);
|
||
ASSERT(ctrl.offset == 0);
|
||
return;
|
||
}
|
||
|
||
// Move to the next phase
|
||
switch (ctrl.phase) {
|
||
// Message in phase
|
||
case BUS::msgin:
|
||
// Bus free phase
|
||
BusFree();
|
||
break;
|
||
|
||
// Data-in Phase
|
||
case BUS::datain:
|
||
// status phase
|
||
Status();
|
||
break;
|
||
|
||
// Status phase
|
||
case BUS::status:
|
||
// Message in phase
|
||
ctrl.length = 1;
|
||
ctrl.blocks = 1;
|
||
ctrl.buffer[0] = (BYTE)ctrl.message;
|
||
MsgIn();
|
||
break;
|
||
|
||
// Other (impossible)
|
||
default:
|
||
ASSERT(FALSE);
|
||
break;
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Receive data
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::Receive()
|
||
{
|
||
int len;
|
||
BOOL result;
|
||
|
||
// REQ is low
|
||
ASSERT(!ctrl.bus->GetREQ());
|
||
ASSERT(!ctrl.bus->GetIO());
|
||
|
||
// Length != 0 if received
|
||
if (ctrl.length != 0) {
|
||
// Receive
|
||
len = ctrl.bus->ReceiveHandShake(
|
||
&ctrl.buffer[ctrl.offset], ctrl.length);
|
||
LOGDEBUG("%s Received %d bytes", __PRETTY_FUNCTION__, len);
|
||
|
||
// If not able to receive all, move to status phase
|
||
if (len != (int)ctrl.length) {
|
||
Error();
|
||
return;
|
||
}
|
||
|
||
// Offset and Length
|
||
ctrl.offset += ctrl.length;
|
||
ctrl.length = 0;
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
LOGDEBUG("%s ctrl.length was 0", __PRETTY_FUNCTION__);
|
||
}
|
||
|
||
// Remove the control block and initialize the result
|
||
ctrl.blocks--;
|
||
result = TRUE;
|
||
|
||
// Process the data out phase
|
||
if (ctrl.phase == BUS::dataout) {
|
||
if (ctrl.blocks == 0) {
|
||
// End with this buffer
|
||
result = XferOut(FALSE);
|
||
} else {
|
||
// Continue to next buffer (set offset, length)
|
||
result = XferOut(TRUE);
|
||
}
|
||
}
|
||
|
||
// If result is false, move to the status phase
|
||
if (!result) {
|
||
Error();
|
||
return;
|
||
}
|
||
|
||
// Continue to receive is block != 0
|
||
if (ctrl.blocks != 0){
|
||
ASSERT(ctrl.length > 0);
|
||
ASSERT(ctrl.offset == 0);
|
||
return;
|
||
}
|
||
|
||
// Move to the next phase
|
||
switch (ctrl.phase) {
|
||
// Data out phase
|
||
case BUS::dataout:
|
||
LOGTRACE("%s transitioning to FlushUnit()",__PRETTY_FUNCTION__);
|
||
// Flush
|
||
FlushUnit();
|
||
|
||
// status phase
|
||
Status();
|
||
break;
|
||
|
||
// Other (impossible)
|
||
default:
|
||
ASSERT(FALSE);
|
||
break;
|
||
}
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Data transfer IN
|
||
// *Reset offset and length
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL SASIDEV::XferIn(BYTE *buf)
|
||
{
|
||
ASSERT(ctrl.phase == BUS::datain);
|
||
LOGTRACE("%s ctrl.cmd[0]=%02X", __PRETTY_FUNCTION__, (unsigned int)ctrl.cmd[0]);
|
||
|
||
// Logical Unit
|
||
DWORD lun = (ctrl.cmd[1] >> 5) & 0x07;
|
||
if (!ctrl.unit[lun]) {
|
||
return FALSE;
|
||
}
|
||
|
||
// Limited to read commands
|
||
switch (ctrl.cmd[0]) {
|
||
// READ(6)
|
||
case eCmdRead6:
|
||
// READ(10)
|
||
case eCmdRead10:
|
||
// Read from disk
|
||
ctrl.length = ctrl.unit[lun]->Read(ctrl.cmd, buf, ctrl.next);
|
||
ctrl.next++;
|
||
|
||
// If there is an error, go to the status phase
|
||
if (ctrl.length <= 0) {
|
||
// Cancel data-in
|
||
return FALSE;
|
||
}
|
||
|
||
// If things are normal, work setting
|
||
ctrl.offset = 0;
|
||
break;
|
||
|
||
// Other (impossible)
|
||
default:
|
||
ASSERT(FALSE);
|
||
return FALSE;
|
||
}
|
||
|
||
// Succeeded in setting the buffer
|
||
return TRUE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Data transfer OUT
|
||
// *If cont=true, reset the offset and length
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
BOOL FASTCALL SASIDEV::XferOut(BOOL cont)
|
||
{
|
||
SCSIBR *bridge;
|
||
|
||
ASSERT(ctrl.phase == BUS::dataout);
|
||
|
||
// Logical Unit
|
||
DWORD lun = (ctrl.cmd[1] >> 5) & 0x07;
|
||
if (!ctrl.unit[lun]) {
|
||
return FALSE;
|
||
}
|
||
|
||
switch ((SASIDEV::scsi_command) ctrl.cmd[0]) {
|
||
case SASIDEV::eCmdModeSelect:
|
||
case SASIDEV::eCmdModeSelect10:
|
||
if (!ctrl.unit[lun]->ModeSelect(
|
||
ctrl.cmd, ctrl.buffer, ctrl.offset)) {
|
||
// MODE SELECT failed
|
||
return FALSE;
|
||
}
|
||
break;
|
||
|
||
case SASIDEV::eCmdWrite6:
|
||
case SASIDEV::eCmdWrite10:
|
||
case SASIDEV::eCmdWriteAndVerify10:
|
||
// If we're a host bridge, use the host bridge's SendMessage10
|
||
// function
|
||
if (ctrl.unit[lun]->IsBridge()) {
|
||
bridge = (SCSIBR*)ctrl.unit[lun];
|
||
if (!bridge->SendMessage10(ctrl.cmd, ctrl.buffer)) {
|
||
// write failed
|
||
return FALSE;
|
||
}
|
||
|
||
// If normal, work setting
|
||
ctrl.offset = 0;
|
||
break;
|
||
}
|
||
|
||
// Special case Write function for DaynaPort
|
||
if (ctrl.unit[lun]->IsDaynaPort()) {
|
||
LOGTRACE("%s Doing special case write for DaynaPort", __PRETTY_FUNCTION__);
|
||
if (!(SCSIDaynaPort*)ctrl.unit[lun]->Write(ctrl.cmd, ctrl.buffer, ctrl.length)) {
|
||
// write failed
|
||
return FALSE;
|
||
}
|
||
LOGTRACE("%s Done with DaynaPort Write", __PRETTY_FUNCTION__);
|
||
|
||
// If normal, work setting
|
||
ctrl.offset = 0;
|
||
ctrl.blocks = 0;
|
||
break;
|
||
}
|
||
|
||
LOGTRACE("%s eCmdWriteAndVerify10 Calling Write... cmd: %02X next: %d", __PRETTY_FUNCTION__, (WORD)ctrl.cmd[0], (int)ctrl.next);
|
||
if (!ctrl.unit[lun]->Write(ctrl.cmd, ctrl.buffer, ctrl.next - 1)) {
|
||
// Write failed
|
||
return FALSE;
|
||
}
|
||
|
||
// If you do not need the next block, end here
|
||
ctrl.next++;
|
||
if (!cont) {
|
||
break;
|
||
}
|
||
|
||
// Check the next block
|
||
ctrl.length = ctrl.unit[lun]->WriteCheck(ctrl.next - 1);
|
||
if (ctrl.length <= 0) {
|
||
// Cannot write
|
||
return FALSE;
|
||
}
|
||
|
||
// If normal, work setting
|
||
ctrl.offset = 0;
|
||
break;
|
||
|
||
// SPECIFY(SASI only)
|
||
case SASIDEV::eCmdInvalid:
|
||
break;
|
||
|
||
case SASIDEV::eCmdSetMcastAddr:
|
||
LOGTRACE("%s Done with DaynaPort Set Multicast Address", __PRETTY_FUNCTION__);
|
||
break;
|
||
default:
|
||
LOGWARN("Received an unexpected command (%02X) in %s", (WORD)ctrl.cmd[0] , __PRETTY_FUNCTION__)
|
||
ASSERT(FALSE);
|
||
break;
|
||
}
|
||
|
||
// Buffer saved successfully
|
||
return TRUE;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Logical unit flush
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void FASTCALL SASIDEV::FlushUnit()
|
||
{
|
||
ASSERT(ctrl.phase == BUS::dataout);
|
||
|
||
// Logical Unit
|
||
DWORD lun = (ctrl.cmd[1] >> 5) & 0x07;
|
||
if (!ctrl.unit[lun]) {
|
||
return;
|
||
}
|
||
|
||
// WRITE system only
|
||
switch ((SASIDEV::scsi_command)ctrl.cmd[0]) {
|
||
case SASIDEV::eCmdWrite6:
|
||
case SASIDEV::eCmdWrite10:
|
||
case SASIDEV::eCmdWriteAndVerify10:
|
||
// Flush
|
||
if (!ctrl.unit[lun]->IsCacheWB()) {
|
||
ctrl.unit[lun]->Flush();
|
||
}
|
||
break;
|
||
case SASIDEV::eCmdModeSelect:
|
||
case SASIDEV::eCmdModeSelect10:
|
||
// Debug code related to Issue #2 on github, where we get an unhandled Model select when
|
||
// the mac is rebooted
|
||
// https://github.com/akuker/RASCSI/issues/2
|
||
LOGWARN("Received \'Mode Select\'\n");
|
||
LOGWARN(" Operation Code: [%02X]\n", (WORD)ctrl.cmd[0]);
|
||
LOGWARN(" Logical Unit %01X, PF %01X, SP %01X [%02X]\n",\
|
||
(WORD)ctrl.cmd[1] >> 5, 1 & ((WORD)ctrl.cmd[1] >> 4), \
|
||
(WORD)ctrl.cmd[1] & 1, (WORD)ctrl.cmd[1]);
|
||
LOGWARN(" Reserved: %02X\n", (WORD)ctrl.cmd[2]);
|
||
LOGWARN(" Reserved: %02X\n", (WORD)ctrl.cmd[3]);
|
||
LOGWARN(" Parameter List Len %02X\n", (WORD)ctrl.cmd[4]);
|
||
LOGWARN(" Reserved: %02X\n",(WORD)ctrl.cmd[5]);
|
||
LOGWARN(" Ctrl Len: %08X\n",(WORD)ctrl.length);
|
||
|
||
if (!ctrl.unit[lun]->ModeSelect(
|
||
ctrl.cmd, ctrl.buffer, ctrl.offset)) {
|
||
// MODE SELECT failed
|
||
LOGWARN("Error occured while processing Mode Select command %02X\n", (unsigned char)ctrl.cmd[0]);
|
||
return;
|
||
}
|
||
break;
|
||
case SASIDEV::eCmdSetIfaceMode:
|
||
LOGWARN("%s Trying to flush a command set interface mode. This should be a daynaport?", __PRETTY_FUNCTION__);
|
||
break;
|
||
case SASIDEV::eCmdSetMcastAddr:
|
||
// TODO: Eventually, we should store off the multicast address configuration data here...
|
||
break;
|
||
default:
|
||
LOGWARN("Received an unexpected flush command %02X!!!!!\n",(WORD)ctrl.cmd[0]);
|
||
// The following statement makes debugging a huge pain. You can un-comment it
|
||
// if you're not trying to add new devices....
|
||
// ASSERT(FALSE);
|
||
break;
|
||
}
|
||
}
|
||
|
||
#ifdef DISK_LOG
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Get the current phase as a string
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
void SASIDEV::GetPhaseStr(char *str)
|
||
{
|
||
switch(this->GetPhase())
|
||
{
|
||
case BUS::busfree:
|
||
strcpy(str,"busfree ");
|
||
break;
|
||
case BUS::arbitration:
|
||
strcpy(str,"arbitration");
|
||
break;
|
||
case BUS::selection:
|
||
strcpy(str,"selection ");
|
||
break;
|
||
case BUS::reselection:
|
||
strcpy(str,"reselection");
|
||
break;
|
||
case BUS::command:
|
||
strcpy(str,"command ");
|
||
break;
|
||
case BUS::execute:
|
||
strcpy(str,"execute ");
|
||
break;
|
||
case BUS::datain:
|
||
strcpy(str,"datain ");
|
||
break;
|
||
case BUS::dataout:
|
||
strcpy(str,"dataout ");
|
||
break;
|
||
case BUS::status:
|
||
strcpy(str,"status ");
|
||
break;
|
||
case BUS::msgin:
|
||
strcpy(str,"msgin ");
|
||
break;
|
||
case BUS::msgout:
|
||
strcpy(str,"msgout ");
|
||
break;
|
||
case BUS::reserved:
|
||
strcpy(str,"reserved ");
|
||
break;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
//---------------------------------------------------------------------------
|
||
//
|
||
// Validate LUN
|
||
//
|
||
//---------------------------------------------------------------------------
|
||
DWORD FASTCALL SASIDEV::GetLun()
|
||
{
|
||
DWORD lun = (ctrl.cmd[1] >> 5) & 0x07;
|
||
if (!ctrl.unit[lun]) {
|
||
throw lunexception(lun);
|
||
}
|
||
|
||
return lun;
|
||
}
|