mirror of
https://github.com/elliotnunn/supermario.git
synced 2024-08-29 23:28:53 +00:00
1626 lines
44 KiB
C
1626 lines
44 KiB
C
/*
|
||
File: SIMmachine.c
|
||
|
||
Contains: routines that implement the SCSI state machine
|
||
|
||
Entry points:
|
||
|
||
|
||
|
||
Written by: Paul Wolf
|
||
|
||
Copyright: © 1992-1993 by Apple Computer, Inc., all rights reserved.
|
||
|
||
Change History (most recent first):
|
||
|
||
<SM22> 12/19/93 DCB Clear hdshkRemainder when we reconnect. This is to help solve
|
||
the handshake across scatter gather boundries problem.
|
||
<SM21> 11/22/93 pdw Rolling in from <MCxx>.
|
||
<MC7> 11/9/93 pdw Fixed a problem with the CD300 rejecting an identify message.
|
||
We now handle it and return an error.
|
||
<MC6> 11/8/93 pdw Added some handling of unexpected bus free.
|
||
<MC5> 11/5/93 pdw Series of attempts and re-attempts to fix various VM/FileShare
|
||
problems.
|
||
<SM20> 10/29/93 DCB <MC> roll-in.
|
||
<MC4> 10/28/93 pdw Added a couple of states for use during Target mode.
|
||
<SM19> 10/14/93 pdw <MC> roll-in.
|
||
<MC3> 10/12/93 pdw Fixed some bugs in some messaging corner cases.
|
||
<MC2> 10/12/93 pdw Added support for Synchronous data transfers, rewrote State
|
||
Machine, message handling etc.
|
||
<SM18> 8/13/93 pdw Changing the message handling in DoComplete.
|
||
<SM17> 7/17/93 pdw Lots of little things. A few big things.
|
||
<SM16> 6/29/93 pdw Massive checkins: Change asynchronicity mechanism to CallMachine
|
||
stack switching mechanism. Adding support for Cold Fusion.
|
||
Rearranging HW/SW Init code. Some code optimizations. Resolving
|
||
with my Ludwig sources.
|
||
<SM15> 5/29/93 PW Adding include of SCSIDebug.h
|
||
<SM14> 5/25/93 DCB Rollin from Ludwig. (The next item below)
|
||
<LW17> 5/21/93 PW Timeout bugs fixed. DebugStr stuff. Last minute target mode
|
||
changes.
|
||
<SM13> 5/5/93 PW Converted names to meanies-friendly names. Updated with latest
|
||
from Ludwig stuff.
|
||
<LW16> 5/1/93 PW Changed xxxResidualLength to xxxResidual.
|
||
<LW15> 4/30/93 DCB Adding CallBusInquiry function so that we don't have to busy the
|
||
HALAction routine.
|
||
<LW14> 4/29/93 DCB Fixing Build - Sorry bout that.
|
||
<LW13> 4/14/93 DCB Added calls the the SetParity HALAction for each reconnect and
|
||
successful initiate.
|
||
<LW12> 3/26/93 PW Rolled in SuperMario changes. Removed useless RejectMsg stuff. A
|
||
few little optimizations. Fixed bug in autosense that showed up
|
||
when deferring completions and fixed bug in ResetDevice. Added
|
||
kWastePhase to bit bucket an unknown phase.
|
||
<LW11> 3/22/93 DCB Adding code to reject an identify message from an unknown
|
||
re-select to fix a test tool bug with the Seagate 400.
|
||
<SM12> 3/20/93 PW Moving CallCompRoutine into SIM so that it can call
|
||
ExitingSIM/EnteringSIM instead of XPT.
|
||
<LW10> 3/22/93 PW Rolling in SuperMario changes. Removing useless RejectMsg stuff.
|
||
Few little optimizations.
|
||
<LW9> 3/1/93 DCB Re-worked message handling. This included changing the use of
|
||
the event field for better tracking of abort and terminate IO
|
||
and fixing the message reject code.
|
||
<LW8> 2/18/93 DCB Added check for Bus Free phase before setting ATN to send a
|
||
message. The c96 will bit bucket the next command after an
|
||
illegal set of ATN causing a sync-wait hang.
|
||
<LW7> 2/17/93 PW Optimizations - Removed some unneeded "what" usages and got rid
|
||
of halPtr in Machine. Also got rid of halPBptr parameter in
|
||
SwitchPhase and Reconnect. Added check for status phz just after
|
||
recon to avoid implied restore pointers (for IBM drives). Fixed
|
||
DontDisconnect bug.
|
||
<SM10> 1/31/93 PW Update from the latest of Ludwig. Also changes required for PDM
|
||
(will update Ludwig with these as needed myself).
|
||
<LW6> 1/27/93 PW Fixed VM 'too many enables' bug by doing the 'double completion
|
||
routine on autosense' stuff the right way - I call VMDisable and
|
||
EnableUserCode at top and bottom of CompAutoSense.
|
||
<LW5> 1/12/93 DCB Fixed handling of IOevent so that timeouts were reported as
|
||
timeouts rather than aborted commands.
|
||
<LW4> 1/8/93 PW Added RestorePointers call upon acceptance of Identify message
|
||
during a reconnect.
|
||
<LW3> 1/4/93 DCB Fixed SetupResetPB so that it fills in the completion routine
|
||
field. The lack of the completion routine caused asynchronous
|
||
ResetDevice calls to get stuck in a SyncWait. This fixes Radar
|
||
bug 1059424 .
|
||
<LW2> 12/17/92 DCB Fixed a bug in the auto-request sense feature which caused a
|
||
sync-wait hang.
|
||
<SM9> 12/9/92 PW Added check for invalid scsiIOFlags.
|
||
<SM8> 12/5/92 PW Changed ValidateDI to return appropriate error instead of
|
||
Boolean.
|
||
<SM7> 11/20/92 DCB Support for async Abort, Terminate and Reset Device
|
||
<SM6> 10/30/92 DCB Added call to Teardown routines to support Direct DMA into a
|
||
user buffer
|
||
<SM5> 10/8/92 PW (dcb) Added support for variable-sized SCSI_IO. Fixed AbortCmd
|
||
bug which required addition of an ioEvent field.
|
||
<SM4> 9/14/92 DCB Added some parameter block validation
|
||
<SM3> 9/11/92 DCB Made numerous changes to the state machine so that the bus
|
||
wouldn't hang after a failed TerminateIO or Abort Command. Also
|
||
added BusDeviceReset.
|
||
<SM2> 8/31/92 DCB Implementing Terminate/Abort IO
|
||
|
||
*/
|
||
|
||
#include <Types.h>
|
||
#include <Memory.h>
|
||
#include <Errors.h>
|
||
#include <SCSIStandard.h>
|
||
#include <SCSI.h>
|
||
|
||
#include "ACAM.h"
|
||
#include "XPT.h"
|
||
#include "XPTpriv.h"
|
||
#include "CUtils.h"
|
||
#include "SCSIGlue.h"
|
||
|
||
#include "SIMCore.h"
|
||
#include "SIMCorePriv.h"
|
||
#include "SIMQ.h"
|
||
|
||
#include "SCSIDebug.h"
|
||
#include "Recorder.h"
|
||
|
||
|
||
/********* External Function Prototypes **********/
|
||
|
||
|
||
/********* Internal-only Function Prototypes **********/
|
||
|
||
void SwitchPhase( SIM_IO * ioPtr, SIMglobals * SIMg);
|
||
void EarlySwitchPhase( SIM_IO * ioPtr, SIMglobals * SIMg);
|
||
void WastePhase( SIM_IO * ioPtr, SIMglobals * SIMg);
|
||
void Reconnect( SIMglobals * SIMg);
|
||
void HandleEarlyMsgIn( SIM_IO * ioPtr, SIMglobals * SIMg);
|
||
void HandleNoIdentifyMsg( SIM_IO * ioPtr, SIMglobals * SIMg);
|
||
void HandleRejectedIdentify( SIM_IO * ioPtr, SIMglobals * SIMg);
|
||
void HandleInitWErr( SIM_IO * ioPtr, SIMglobals * SIMg);
|
||
|
||
/*********************************************************************************
|
||
Machine - the actual SSM!
|
||
*********************************************************************************/
|
||
/*
|
||
The SSM works with the HAL to transact a SCSI connection. The communication
|
||
between the SSM and HAL occur via a HALactionPB which is allocated in the
|
||
SIM's globals. The same HALactionPB is used from the time of Initiation to
|
||
Disconnection.
|
||
*/
|
||
|
||
Boolean // freeTheMachine
|
||
TheMachine( OSErr * finalStatus, SIM_IO ** completedIO, SIMglobals * SIMg)
|
||
{
|
||
HALresult result;
|
||
HALactions halAct;
|
||
SIM_IO * ioPtr;
|
||
uchar targetID;
|
||
uchar LUN;
|
||
|
||
|
||
ioPtr = SIMg->halPB.ioPtr; // <SM2>
|
||
targetID = ioPtr->scsiDevice.targetID;
|
||
|
||
/********** STATE MACHINE **********/
|
||
|
||
do
|
||
{
|
||
result = SIMg->halPB.result;
|
||
|
||
/* If we are connected to an io which needs a message - assert ATN
|
||
SwitchPhase will figure out how to actually send it
|
||
*/
|
||
if (ioPtr) if (ioPtr->pendingMsgPB) // guarantee that this is the first test of the if
|
||
{
|
||
if (SIMg->state != kIdle &&
|
||
SIMg->state != kGotR_selected &&
|
||
(ioPtr->ioEvent & kmMsgSent) == 0 && // <LW9>
|
||
SIMg->halPB.phase != kBusFreePhase )
|
||
{
|
||
if (SIMg->halPB.phase == kStatusPhase)
|
||
{
|
||
switch( ((SCSIHdr *)(ioPtr->pendingMsgPB))->scsiFunctionCode) { // <SM7>
|
||
|
||
case SCSITerminateIO:
|
||
((SCSIHdr *)(ioPtr->pendingMsgPB))->scsiResult = scsiUnableToTerminate;
|
||
IfRecordEvent( (long)ioPtr->pendingMsgPB, (long)'TrmC');
|
||
CallCompRoutine( (SCSI_IO *)ioPtr->pendingMsgPB);
|
||
(SCSI_IO *)ioPtr->pendingMsgPB = nil;
|
||
break;
|
||
case SCSIAbortCommand:
|
||
((SCSIHdr *)(ioPtr->pendingMsgPB))->scsiResult = scsiUnableToAbort;
|
||
IfRecordEvent( (long)ioPtr->pendingMsgPB, (long)'AbtC');
|
||
CallCompRoutine( (SCSI_IO *)ioPtr->pendingMsgPB);
|
||
(SCSI_IO *)ioPtr->pendingMsgPB = nil;
|
||
break;
|
||
case SCSIResetDevice:
|
||
break;
|
||
} // <SM7>
|
||
}
|
||
else { // (not status phase)
|
||
CallHALAssertATN( SIMg->HALinfo.HALstaticPtr);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
IfRecordEvent( (long)((SIMg->state<<16) + result), (long)'Mach');
|
||
|
||
switch (SIMg->state)
|
||
{
|
||
|
||
//==== IDLE ====
|
||
|
||
case kIdle:
|
||
|
||
if (ioPtr->scsiFunctionCode != SCSIOldCall)
|
||
{
|
||
//———— NEW API ——————
|
||
|
||
// Set up for sending an Identify message during Initiate
|
||
// depending on our default disc, we check the other Do/Don’t flag to make sure
|
||
|
||
if (SIMg->defaultDisc) {
|
||
if ( ioPtr->scsiFlags & scsiDontDisconnect) // if default=Do but client says Don’t
|
||
SIMg->halPB.msg[0] = IdentifyMsg( false, ioPtr->scsiDevice.LUN);
|
||
else
|
||
SIMg->halPB.msg[0] = IdentifyMsg( true, ioPtr->scsiDevice.LUN);
|
||
}
|
||
else {
|
||
if ( ioPtr->scsiFlags & scsiDoDisconnect) // if default=Don’t but client says Do
|
||
SIMg->halPB.msg[0] = IdentifyMsg( true, ioPtr->scsiDevice.LUN);
|
||
else
|
||
SIMg->halPB.msg[0] = IdentifyMsg( false, ioPtr->scsiDevice.LUN);
|
||
}
|
||
|
||
if (SIMg->syncRAoffset[targetID]) {
|
||
CallHALSyncConfig( SIMg->syncRAoffset[targetID],
|
||
SIMg->syncPeriod [targetID], SIMg->HALinfo.HALstaticPtr );
|
||
}
|
||
SIMg->halPB.msgOutLen = 1; // only 1 byte for now, Identify
|
||
SIMg->halPB.sendCDB = true;
|
||
|
||
SIMg->state = kInitiatingNormal;
|
||
|
||
|
||
// Check to see if this is a forced reconnection for the purpose of
|
||
// issuing an abort, terminate io or reset device message
|
||
|
||
if( ioPtr->pendingMsgPB )
|
||
{
|
||
switch( ((SCSI_PB *)ioPtr->pendingMsgPB)->scsiFunctionCode) { // <SM7>
|
||
|
||
case SCSITerminateIO:
|
||
SIMg->halPB.msg[1] = kTerminateIOProcessMsg;
|
||
break;
|
||
case SCSIAbortCommand:
|
||
SIMg->halPB.msg[1] = kAbortMsg;
|
||
break;
|
||
case SCSIResetDevice:
|
||
SIMg->halPB.msg[1] = kBusDeviceResetMsg;
|
||
break;
|
||
|
||
}
|
||
SIMg->halPB.msgOutLen = 2;
|
||
SIMg->halPB.sendCDB = false;
|
||
|
||
ioPtr->ioEvent |= (kmMsgSent); // we'll undo this if it fails
|
||
|
||
SIMg->state = kInitiatingAsyncMsg;
|
||
}
|
||
else
|
||
{
|
||
// Check for need to perform Synchronous Data Transfer negotiation
|
||
|
||
if ( (ioPtr->scsiFlags & (scsiInitiateSyncData | scsiDisableSyncData)) ||
|
||
SIMg->needNegot[targetID] )
|
||
{
|
||
SIMg->halPB.msg[1] = 0x01; // extended message
|
||
SIMg->halPB.msg[2] = 0x03; // length
|
||
SIMg->halPB.msg[3] = 0x01; // SDTR
|
||
SIMg->halPB.msg[4] = SIMg->HALinfo.minPeriodFactor; // transfer period
|
||
if (ioPtr->scsiFlags & scsiDisableSyncData) {
|
||
SIMg->halPB.msg[5] = 0; // asynchronous
|
||
}
|
||
else {
|
||
SIMg->halPB.msg[5] = SIMg->HALinfo.syncRAoffset; // REQ/ACK offset
|
||
}
|
||
SIMg->halPB.msgOutLen = 6;
|
||
SIMg->halPB.sendCDB = false;
|
||
|
||
SIMg->state = kInitiatingSDTR;
|
||
}
|
||
}
|
||
// Start initiation process (this could come back w/ a recon)
|
||
|
||
ioPtr->ioStat = kAttemptingInitiation;
|
||
CallHALaction( kInitiate, &SIMg->halPB, SIMg);
|
||
}
|
||
|
||
else // ioPtr->scsiFunctionCode == SCSIOldCall
|
||
{
|
||
//———— OLD API ——————
|
||
|
||
ioPtr->ioStat = kAttemptingInitiation;
|
||
SIMg->state = kInitiatingOldCall;
|
||
CallHALaction( kSelect, &SIMg->halPB, SIMg);
|
||
}
|
||
|
||
break;
|
||
|
||
|
||
//==== GotR_selected In Progress ====
|
||
|
||
case kGotR_selected:
|
||
|
||
CallHALaction( kGetReconnectInfo, &SIMg->halPB, SIMg); // always synchronous call
|
||
if ( SIMg->halPB.result == kHALreselected) { // really was a reconnect
|
||
Reconnect( SIMg);
|
||
ioPtr = SIMg->halPB.ioPtr; // Reconnect can change which IO we're doing
|
||
targetID = ioPtr->scsiDevice.targetID;
|
||
}
|
||
else if ( SIMg->halPB.result == kHALselectedAsTarget) {
|
||
SIMg->state = kHandlingSelected;
|
||
SIMg->pushedState = kFreeMachine; // after select is done, free the machine
|
||
}
|
||
|
||
break;
|
||
|
||
|
||
//==== HandlingSelected In Progress ====
|
||
|
||
case kHandlingSelected:
|
||
|
||
SIMg->r_selectWaiting = false;
|
||
CallHALaction( kHandleSelected, &SIMg->halPB, SIMg);
|
||
SIMg->state = SIMg->pushedState;
|
||
|
||
break;
|
||
|
||
|
||
//==== INITIATE In Progress - Normal ====
|
||
|
||
case kInitiatingNormal:
|
||
|
||
if (!result) //== noErr
|
||
{
|
||
ioPtr->ioStat = kSentCommand;
|
||
SwitchPhase( ioPtr, SIMg);
|
||
}
|
||
else if (result == kHALreselected)
|
||
{
|
||
ioPtr->ioStat = kPBidle; // go back to Idle for this one
|
||
Reconnect( SIMg);
|
||
ioPtr = SIMg->halPB.ioPtr; // Reconnect can change which IO we're doing
|
||
targetID = ioPtr->scsiDevice.targetID;
|
||
}
|
||
else
|
||
{
|
||
HandleInitWErr( ioPtr, SIMg);
|
||
ioPtr = SIMg->halPB.ioPtr; // Reconnect can change which IO we're doing
|
||
targetID = ioPtr->scsiDevice.targetID;
|
||
}
|
||
break;
|
||
|
||
|
||
|
||
//==== INITIATE In Progress - SDTR ====
|
||
|
||
case kInitiatingSDTR:
|
||
|
||
if (!result)
|
||
{
|
||
if (SIMg->halPB.msgInLen == 6)
|
||
{
|
||
if ((SIMg->halPB.msg[1] == 0x01) && (SIMg->halPB.msg[3] == 0x01)) {
|
||
SIMg->syncPeriod [targetID] = SIMg->halPB.msg[4]; // transfer period
|
||
SIMg->syncRAoffset[targetID] = SIMg->halPB.msg[5]; // REQ/ACK offset
|
||
|
||
CallHALSyncConfig( SIMg->syncRAoffset[ioPtr->scsiDevice.targetID],
|
||
SIMg->syncPeriod [ioPtr->scsiDevice.targetID], SIMg->HALinfo.HALstaticPtr );
|
||
|
||
CallHALaction( kAcceptMsg, &SIMg->halPB, SIMg);
|
||
}
|
||
}
|
||
else {
|
||
SIMg->syncRAoffset[targetID] = 0;
|
||
if ((SIMg->halPB.msgInLen == 1) && (SIMg->halPB.msg[0] == kMsgRejectMsg))
|
||
{
|
||
CallHALaction( kAcceptMsg, &SIMg->halPB, SIMg);
|
||
}
|
||
}
|
||
EarlySwitchPhase( ioPtr, SIMg);
|
||
}
|
||
else if (result == kHALreselected)
|
||
{
|
||
ioPtr->ioStat = kPBidle; // go back to Idle for this one
|
||
Reconnect( SIMg);
|
||
ioPtr = SIMg->halPB.ioPtr; // Reconnect can change which IO we're doing
|
||
targetID = ioPtr->scsiDevice.targetID;
|
||
}
|
||
else
|
||
{
|
||
HandleInitWErr( ioPtr, SIMg);
|
||
ioPtr = SIMg->halPB.ioPtr; // Reconnect can change which IO we're doing
|
||
targetID = ioPtr->scsiDevice.targetID;
|
||
}
|
||
|
||
break;
|
||
|
||
|
||
//==== INITIATE In Progress - AsyncMsg ====
|
||
|
||
case kInitiatingAsyncMsg:
|
||
|
||
if (!result)
|
||
{
|
||
if (SIMg->halPB.phase == kBusFreePhase)
|
||
{
|
||
switch( ((SCSI_PB *)ioPtr->pendingMsgPB)->scsiFunctionCode)
|
||
{
|
||
case SCSITerminateIO:
|
||
ioPtr->ioEvent |= (kmTerminated | kmMsgSent);
|
||
// Remove this IO from the Disconnected array since we aborted cmd
|
||
SIMg->discIOs [targetID] [ioPtr->scsiDevice.LUN] = 0;
|
||
SIMg->LUNstate[targetID] [ioPtr->scsiDevice.LUN] &= ~km_LUNdisconnected;
|
||
break;
|
||
|
||
case SCSIAbortCommand:
|
||
ioPtr->ioEvent |= (kmAborted | kmMsgSent);
|
||
// Remove this IO from the Disconnected array since we aborted cmd
|
||
SIMg->discIOs [targetID] [ioPtr->scsiDevice.LUN] = 0;
|
||
SIMg->LUNstate[targetID] [ioPtr->scsiDevice.LUN] &= ~km_LUNdisconnected;
|
||
break;
|
||
|
||
case SCSIResetDevice:
|
||
ioPtr->ioEvent |= (kmBDRSent | kmMsgSent);
|
||
for( LUN = 0; LUN < 8; LUN+=1 ) {
|
||
SIMg->discIOs [targetID][LUN] = 0;
|
||
SIMg->LUNstate[targetID][LUN] &= ~km_LUNdisconnected;
|
||
}
|
||
break;
|
||
}
|
||
*finalStatus = result;
|
||
*completedIO = ioPtr;
|
||
return (true);
|
||
}
|
||
else if ((SIMg->halPB.phase == kMessageInPhaseNACK) && (SIMg->halPB.msg[0] == kMsgRejectMsg))
|
||
{
|
||
CallHALaction( kAcceptMsg, &SIMg->halPB, SIMg);
|
||
SIMg->state = kAcceptingRejectedAsyncMsg;
|
||
break;
|
||
}
|
||
else {
|
||
*finalStatus = scsiUnableToAbort;
|
||
WastePhase( ioPtr, SIMg);
|
||
}
|
||
}
|
||
else if (result == kHALreselected)
|
||
{
|
||
ioPtr->ioStat = kPBidle; // go back to Idle for this one
|
||
Reconnect( SIMg);
|
||
ioPtr = SIMg->halPB.ioPtr; // Reconnect can change which IO we're doing
|
||
targetID = ioPtr->scsiDevice.targetID;
|
||
}
|
||
else
|
||
{
|
||
HandleInitWErr( ioPtr, SIMg);
|
||
ioPtr = SIMg->halPB.ioPtr; // Reconnect can change which IO we're doing
|
||
targetID = ioPtr->scsiDevice.targetID;
|
||
}
|
||
|
||
break;
|
||
|
||
|
||
//==== INITIATE In Progress - OldCall ====
|
||
|
||
case kInitiatingOldCall:
|
||
|
||
ioPtr->scsiCurrentPhase = SIMg->halPB.phase;
|
||
if (result == noErr)
|
||
{
|
||
SIMg->state = kAwaitingOldCall;
|
||
ioPtr->ioStat = kSelectComplete;
|
||
ioPtr->scsiOldCallResult = noErr;
|
||
return (false);
|
||
}
|
||
else if (result == kHALreselected) // i.e. we got reselected
|
||
{
|
||
ioPtr->ioStat = kPBidle; // go back to Idle for this one
|
||
Reconnect( SIMg);
|
||
ioPtr = SIMg->halPB.ioPtr; // Reconnect can change which IO we're doing
|
||
targetID = ioPtr->scsiDevice.targetID;
|
||
break;
|
||
}
|
||
else if (result == kHALselectedAsTarget) // we got selected
|
||
{
|
||
SIMg->state = kHandlingSelected;
|
||
SIMg->pushedState = kIdle; // after select is done, retry this IO initiation
|
||
break;
|
||
}
|
||
else if (result == kHALselectFld)
|
||
{
|
||
*finalStatus = scsiSelectTimeout;
|
||
}
|
||
else
|
||
{
|
||
*finalStatus = result;
|
||
}
|
||
|
||
ioPtr->scsiOldCallResult = *finalStatus;
|
||
|
||
if (SIMg->halPB.phase == kBusFreePhase)
|
||
{
|
||
*completedIO = ioPtr;
|
||
return (true);
|
||
}
|
||
else {
|
||
return (false);
|
||
}
|
||
break;
|
||
|
||
|
||
//==== Accepting Msg after Rejected Async Msg ====
|
||
|
||
case kAcceptingRejectedAsyncMsg:
|
||
|
||
if (SIMg->halPB.phase == kBusFreePhase)
|
||
{
|
||
ioPtr->ioEvent |= (kmMsgSent);
|
||
*finalStatus = scsiMessageRejectReceived;
|
||
*completedIO = ioPtr;
|
||
return (true);
|
||
}
|
||
WastePhase(ioPtr, SIMg);
|
||
break;
|
||
|
||
|
||
//==== Accepting Msg after Rejected Identify Msg ====
|
||
|
||
case kAcceptingRejectedIdentify:
|
||
|
||
if (SIMg->halPB.phase == kMessageOutPhase)
|
||
{
|
||
SIMg->halPB.msg[0] = kAbortMsg;
|
||
SIMg->halPB.msgOutLen = 1;
|
||
SIMg->state = kAbortingRejectedIdentify;
|
||
CallHALaction( kSendMsgOut, &SIMg->halPB, SIMg);
|
||
|
||
} else if (SIMg->halPB.phase == kBusFreePhase)
|
||
{
|
||
*finalStatus = scsiIdentifyMessageRejected;
|
||
*completedIO = ioPtr;
|
||
return (true);
|
||
}
|
||
break;
|
||
|
||
|
||
//==== Aborting IO after Rejected Identify Msg ====
|
||
|
||
case kAbortingRejectedIdentify:
|
||
|
||
if (SIMg->halPB.phase == kBusFreePhase)
|
||
{
|
||
*finalStatus = scsiIdentifyMessageRejected;
|
||
*completedIO = ioPtr;
|
||
return (true);
|
||
}
|
||
else if (SIMg->halPB.phase == kMessageInPhaseNACK)
|
||
{
|
||
//if (SIMg->halPB.msg[0]==kMsgRejectMsg) enough of this, just accept it
|
||
CallHALaction( kAcceptMsg, &SIMg->halPB, SIMg);
|
||
SIMg->state = kEarlySwitchingPhase;
|
||
}
|
||
break;
|
||
|
||
|
||
//==== RECONNECT - Accepting Identify Message ====
|
||
|
||
case kReconAcceptingMsg:
|
||
|
||
if (ioPtr->ioStat == kDisconnected)
|
||
{
|
||
// for IBM drives, that don't do SaveDataPointer on last data connection of a write
|
||
if (SIMg->halPB.phase != kStatusPhase)
|
||
CallHALaction( kRestorePointers, &SIMg->halPB, SIMg);
|
||
SwitchPhase( ioPtr, SIMg);
|
||
}
|
||
else
|
||
EarlySwitchPhase( ioPtr, SIMg);
|
||
|
||
break;
|
||
|
||
|
||
//==== Sending Message ====
|
||
|
||
case kSendingEarlyKillMsg:
|
||
case kSendingKillMsg:
|
||
|
||
if (!result)
|
||
{
|
||
switch( ((SCSI_PB *)(ioPtr->pendingMsgPB))->scsiFunctionCode) {
|
||
|
||
case SCSITerminateIO:
|
||
ioPtr->ioEvent |= (kmTerminated | kmMsgSent);
|
||
// Remove this IO from the Disconnected array since we aborted cmd
|
||
SIMg->discIOs [targetID] [ioPtr->scsiDevice.LUN] = 0;
|
||
SIMg->LUNstate[targetID] [ioPtr->scsiDevice.LUN] &= ~km_LUNdisconnected;
|
||
break;
|
||
|
||
case SCSIAbortCommand:
|
||
ioPtr->ioEvent |= (kmAborted | kmMsgSent);
|
||
// Remove this IO from the Disconnected array since we aborted cmd
|
||
SIMg->discIOs [targetID] [ioPtr->scsiDevice.LUN] = 0;
|
||
SIMg->LUNstate[targetID] [ioPtr->scsiDevice.LUN] &= ~km_LUNdisconnected;
|
||
break;
|
||
|
||
case SCSIResetDevice:
|
||
ioPtr->ioEvent |= (kmBDRSent | kmMsgSent);
|
||
for( LUN = 0; LUN < 8; LUN+=1 ) {
|
||
SIMg->discIOs [targetID][LUN] = 0;
|
||
SIMg->LUNstate[targetID][LUN] &= ~km_LUNdisconnected;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
if (SIMg->halPB.phase == kBusFreePhase) {
|
||
*finalStatus = result;
|
||
*completedIO = ioPtr;
|
||
return (true);
|
||
}
|
||
else {
|
||
if (SIMg->state == kSendingEarlyKillMsg)
|
||
EarlySwitchPhase( ioPtr, SIMg);
|
||
else
|
||
SwitchPhase( ioPtr, SIMg);
|
||
}
|
||
break;
|
||
|
||
|
||
//==== Sending Initiator Detected Error Msg ====
|
||
|
||
case kSendingDetectedErrorMsg:
|
||
|
||
if (!result)
|
||
{
|
||
IfDebugStr("\pkSendingDetectedErrorMsg not supported");
|
||
}
|
||
break;
|
||
|
||
|
||
//==== Sending Command ====
|
||
|
||
case kSendingCommand:
|
||
|
||
ioPtr->ioStat = kSentCommand;
|
||
SwitchPhase( ioPtr, SIMg);
|
||
break;
|
||
|
||
|
||
//==== Sending Message ====
|
||
|
||
case kSendingMsg:
|
||
|
||
SwitchPhase( ioPtr, SIMg);
|
||
break;
|
||
|
||
|
||
//==== Rejecting Early Message In ====
|
||
|
||
case kRejectingEarlyMsg:
|
||
|
||
if (SIMg->halPB.phase == kMessageOutPhase)
|
||
{
|
||
SIMg->state = kEarlySwitchingPhase;
|
||
SIMg->halPB.msg[0] = kMsgRejectMsg;
|
||
SIMg->halPB.msgOutLen = 1;
|
||
CallHALaction( kSendMsgOut, &SIMg->halPB, SIMg);
|
||
}
|
||
else
|
||
EarlySwitchPhase( ioPtr, SIMg);
|
||
|
||
break;
|
||
|
||
|
||
//==== Accepting Early SDTR ====
|
||
|
||
case kAcceptingEarlySDTR:
|
||
case kAcceptingSDTR:
|
||
|
||
if (result)
|
||
{
|
||
SIMg->syncRAoffset[targetID] = 0; // Something went wrong - target is asynchronous
|
||
}
|
||
EarlySwitchPhase( ioPtr, SIMg);
|
||
|
||
break;
|
||
|
||
|
||
//==== Rejecting a message ====
|
||
|
||
case kReconRejectingMsg:
|
||
case kRejectingDataMsg:
|
||
|
||
switch( SIMg->halPB.phase ) {
|
||
|
||
case kMessageInPhaseNACK: // in case of multiple message-in bytes
|
||
CallHALaction( kAcceptMsg, &SIMg->halPB, SIMg);
|
||
break;
|
||
case kMessageOutPhase:
|
||
SIMg->state = kSendingMsg;
|
||
SIMg->halPB.msg[0] = kMsgRejectMsg;
|
||
SIMg->halPB.msgOutLen = 1;
|
||
CallHALaction( kSendMsgOut, &SIMg->halPB, SIMg);
|
||
break;
|
||
default:
|
||
IfDebugStr("\pBogus phase while rejecting message!");
|
||
SwitchPhase( ioPtr, SIMg);
|
||
break;
|
||
}
|
||
break;
|
||
|
||
|
||
//==== Switching Phase ====
|
||
|
||
case kSwitchingPhase:
|
||
|
||
SwitchPhase( ioPtr, SIMg);
|
||
break;
|
||
|
||
|
||
//==== Doing Data ====
|
||
|
||
case kDoingData:
|
||
|
||
if (!result) {
|
||
SwitchPhase( ioPtr, SIMg);
|
||
}
|
||
else if (result == scsiWrongDirection) {
|
||
ioPtr->scsiDataResidual = -ioPtr->scsiDataResidual;
|
||
ioPtr->SIMprivFlags |= kmDataDone; // to get SwitchPhase to bitbucket
|
||
ioPtr->firstError = scsiWrongDirection;
|
||
SwitchPhase( ioPtr, SIMg);
|
||
}
|
||
else if (result == scsiUnexpectedBusFree) {
|
||
SIMg->state = kWentBusFree;
|
||
}
|
||
break;
|
||
|
||
|
||
//==== Early Switching Phase ====
|
||
|
||
case kEarlySwitchingPhase:
|
||
|
||
EarlySwitchPhase( ioPtr, SIMg);
|
||
break;
|
||
|
||
|
||
//==== Completing ====
|
||
|
||
case kGettingStatus:
|
||
|
||
if (result == noErr) // this means we got a status, msg and then bus free
|
||
{
|
||
*finalStatus = noErr;
|
||
*completedIO = ioPtr;
|
||
IfRecordEvent( (long)ioPtr, (long)'GtSt');
|
||
return (true);
|
||
}
|
||
else
|
||
{
|
||
IfDebugStr("\pError while kGettingStatus");
|
||
*finalStatus = result;
|
||
*completedIO = ioPtr;
|
||
IfRecordError( result);
|
||
return (true);
|
||
}
|
||
break;
|
||
|
||
|
||
//==== Disconnecting Before Sending Command ====
|
||
|
||
case kDisconnectingB4Command:
|
||
|
||
if (SIMg->halPB.phase == kBusFreePhase) {
|
||
*finalStatus = 0;
|
||
*completedIO = 0;
|
||
return (true);
|
||
}
|
||
else { // huh? we got a disconnect msg, I expected to disconnect!
|
||
EarlySwitchPhase( ioPtr, SIMg); // only diff between this and next
|
||
}
|
||
break;
|
||
|
||
|
||
//==== Disconnecting ====
|
||
|
||
case kDisconnecting:
|
||
|
||
if (SIMg->halPB.phase == kBusFreePhase) {
|
||
*finalStatus = 0;
|
||
*completedIO = 0;
|
||
return (true);
|
||
}
|
||
else { // huh? we got a disconnect msg, I expected to disconnect!
|
||
SwitchPhase( ioPtr, SIMg);
|
||
}
|
||
break;
|
||
|
||
|
||
//==== Went Bus Free Unexpectedly ====
|
||
|
||
// Error condition
|
||
// This is used to return from an unexpected bus free
|
||
|
||
case kWentBusFree:
|
||
*finalStatus = scsiUnexpectedBusFree;
|
||
// fall thru
|
||
|
||
//==== Bus Free From WastePhase ====
|
||
|
||
case kWastedToBusFree:
|
||
*completedIO = ioPtr;
|
||
return (true);
|
||
break;
|
||
|
||
|
||
//==== FreeMachine ====
|
||
|
||
case kFreeMachine:
|
||
*finalStatus = noErr;
|
||
*completedIO = 0; // no IOpb was being worked on, none to complete
|
||
return (true); // free the machine
|
||
break;
|
||
|
||
|
||
//==== Returning from failed Select ====
|
||
|
||
// Error condition
|
||
// Used by HandleInitWErr to return from an unexpected bus free
|
||
|
||
case kFailingSelect:
|
||
*finalStatus = scsiSelectTimeout;
|
||
*completedIO = ioPtr;
|
||
return (true);
|
||
break;
|
||
|
||
|
||
//==== AwaitingOldCall ====
|
||
|
||
case kAwaitingOldCall:
|
||
|
||
SIMg->state = kPerformingOldCall;
|
||
//
|
||
switch (ioPtr->scsiSelector)
|
||
{
|
||
case kSCSICmd:
|
||
if (ioPtr->scsiCurrentPhase != kCommandPhase) {
|
||
result = scPhaseErr;
|
||
break;
|
||
}
|
||
halAct = kCommand;
|
||
break;
|
||
|
||
case kSCSIComplete:
|
||
halAct = kComplete;
|
||
ioPtr->scTimer = ioPtr->scsiTimeout; /* Start timer <LW9> */
|
||
break;
|
||
|
||
case kSCSIRead:
|
||
case kSCSIRBlind:
|
||
halAct = kDataIn;
|
||
break;
|
||
|
||
case kSCSIWrite:
|
||
case kSCSIWBlind:
|
||
halAct = kDataOut;
|
||
break;
|
||
|
||
case kSCSIMsgIn:
|
||
if (ioPtr->scsiCurrentPhase != kMessageInPhase) {
|
||
result = scPhaseErr;
|
||
break;
|
||
}
|
||
// we autoMsgIn'd the message already so we just move it
|
||
SIMg->halPB.msgInLen -= 1;
|
||
ioPtr->scsiSCSImessage = SIMg->halPB.msg[0];
|
||
// then we accept it, going to the next phase
|
||
halAct = kAcceptMsg;
|
||
break;
|
||
|
||
case kSCSIMsgOut: // need to get buffer set up in HALactionPB
|
||
if (ioPtr->scsiCurrentPhase != kMessageOutPhase) {
|
||
result = scPhaseErr;
|
||
break;
|
||
}
|
||
SIMg->halPB.msg[0] = ioPtr->scsiSCSImessage;
|
||
SIMg->halPB.msgOutLen = 1;
|
||
halAct = kSendMsgOut;
|
||
break;
|
||
|
||
// These selectors should never get down to the SIM - the XPT handles them
|
||
case kSCSIStat: // data generated by XPT
|
||
case kSCSIReset: // sent as ResetBus request(s)
|
||
case kSCSIGet: // ignored
|
||
case kSCSISelect: // sent as Initiate
|
||
case kSCSISelAtn: // sent as Initiate
|
||
default:
|
||
IfDebugStr("\pUnknown old call selector in kAwaitingOldCall state");
|
||
SysError( dsIOCoreErr);
|
||
break;
|
||
}
|
||
|
||
CallHALaction( halAct, &SIMg->halPB, SIMg);
|
||
|
||
break;
|
||
|
||
|
||
//==== PerformingOldCall ====
|
||
|
||
case kPerformingOldCall:
|
||
|
||
if (SIMg->halPB.phase == kMessageInPhaseNACK)
|
||
ioPtr->scsiCurrentPhase = kMessageInPhase; // convert (we already got the msg)
|
||
else
|
||
ioPtr->scsiCurrentPhase = SIMg->halPB.phase;
|
||
|
||
SIMg->state = kAwaitingOldCall;
|
||
*finalStatus = result;
|
||
ioPtr->scsiOldCallResult = *finalStatus;
|
||
|
||
if (SIMg->halPB.phase == kBusFreePhase)
|
||
{
|
||
*completedIO = ioPtr;
|
||
return (true); // return out of machine and DO free it
|
||
}
|
||
return (false); // return out of machine but DON'T free it
|
||
break;
|
||
|
||
|
||
//==== Getting SCSIMsgIn message ====
|
||
|
||
case kGettingSCSIMsgIn:
|
||
|
||
SIMg->state = kPerformingOldCall;
|
||
CallHALaction( kAcceptMsg, &SIMg->halPB, SIMg);
|
||
break;
|
||
|
||
|
||
//==== BitBucketing 1 phase ====
|
||
|
||
case kWastingPhase:
|
||
|
||
WastePhase( ioPtr, SIMg);
|
||
break;
|
||
|
||
|
||
//==== ? ====
|
||
|
||
default:
|
||
IfDebugStr("\pUnknown state");
|
||
SIMg->state = SIMg->state;
|
||
|
||
} //============ end of switch on SIMg->state =================
|
||
|
||
} while (true);
|
||
}
|
||
|
||
|
||
/*********************************************************************************
|
||
Reconnect -
|
||
*********************************************************************************/
|
||
/*
|
||
This is kind of tricky. This routine can be called at various stages in the
|
||
reconnect process to determine what to do next. This can be thought of as
|
||
another state but is not a part of TheMachine to avoid code duplication.
|
||
|
||
There are two possible ways to end up here - either by a Reconnect interrupt
|
||
causing the HAL to send a ReconnectISR call to the SSM or by the SSM receiving
|
||
a Reconnect event in response to a HALinitiate or Select call.
|
||
|
||
In the first case, the HAL will call the ReconnectISR entry point in the SSM
|
||
which sets a flag and tries to start up the Machine.
|
||
|
||
In the second case, the SSM will be kIdle and be working on a kNewIO event,
|
||
forming a new HALactionPB (in the SIM's globals) and issuing an Initiate request
|
||
to the HAL. If a reconnect occurs after the StartMachine() call is made by
|
||
SIMCore and before the arbitration of the bus has occurred, then the HAL ISR's
|
||
call to ReconnectISR() will see that the Machine is busy, and return to the HAL
|
||
without starting up the Machine (it's already running with the Initiate). The
|
||
HAL will be in the middle of or shortly thereafter receiving the HALinitiate call.
|
||
The HAL will respond to that call by placing the recon vals in the HALactionPB
|
||
and calling Machine() which will call Reconnect() to complete the reconnection.
|
||
*/
|
||
|
||
void
|
||
Reconnect( SIMglobals * SIMg)
|
||
{
|
||
SIMg->r_selectWaiting = false;
|
||
|
||
// for c96
|
||
if (SIMg->halPB.phase == kMessageInPhaseNACK) // got Identify, need a MsgAccept
|
||
{
|
||
SIMg->reconTargetID = SIMg->halPB.selectorID;
|
||
SIMg->reconLUN = IdentifyToLUN( SIMg->halPB.msg[0]);
|
||
|
||
|
||
// Get the IO that was talking to this Target/LUN
|
||
|
||
SIMg->halPB.ioPtr = SIMg->discIOs[SIMg->reconTargetID][SIMg->reconLUN];
|
||
|
||
// If unknown, reject, if known accept and complete the reconnection
|
||
|
||
if ( SIMg->halPB.ioPtr == 0)
|
||
{
|
||
// this is apparently an unknown IO, we'll reject the Identify
|
||
// should do a message reject message (i.e. setAtn, send MsgRej) (per SCSI-2?)
|
||
|
||
// !!! We need to fill in the ioPtr field in the HalActionPB so that if
|
||
// we need to do anything other than reject the message we won't explode!
|
||
|
||
SIMg->state = kReconRejectingMsg;
|
||
CallHALAssertATN( SIMg->HALinfo.HALstaticPtr);
|
||
CallHALaction( kAcceptMsg, &SIMg->halPB, SIMg);
|
||
}
|
||
else // we have an IO to reconnect to
|
||
{
|
||
ReconnectIO( SIMg->reconTargetID, SIMg->reconLUN);
|
||
|
||
CallHALSyncConfig( SIMg->syncRAoffset[SIMg->halPB.ioPtr->scsiDevice.targetID],
|
||
SIMg->syncPeriod [SIMg->halPB.ioPtr->scsiDevice.targetID], SIMg->HALinfo.HALstaticPtr );
|
||
|
||
SIMg->state = kReconAcceptingMsg;
|
||
SIMg->halPB.ioPtr->hdshkRemainder = 0; // Handshake gets re-started on a reconnect...
|
||
CallHALaction( kSetParity, &SIMg->halPB, SIMg);
|
||
CallHALaction( kAcceptMsg, &SIMg->halPB, SIMg);
|
||
}
|
||
}
|
||
|
||
else if (SIMg->halPB.phase == kMessageInPhase) // need to get Identify
|
||
{
|
||
IfDebugStr("\pMessageInPhase after reconnect");
|
||
SysError( dsIOCoreErr);
|
||
}
|
||
|
||
else // not message phase at all
|
||
{
|
||
IfDebugStr("\pNot message phase after reconnect");
|
||
SysError( dsIOCoreErr);
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
/*********************************************************************************
|
||
EarlySwitchPhase - we've sent the Identify but not yet the Command
|
||
*********************************************************************************/
|
||
|
||
void
|
||
EarlySwitchPhase( SIM_IO * ioPtr, SIMglobals * SIMg)
|
||
{
|
||
Boolean assertATN;
|
||
|
||
IfRecordEvent( (long)(SIMg->halPB.phase), (long)'ESwP');
|
||
|
||
switch (SIMg->halPB.phase) {
|
||
|
||
case kDataInPhase:
|
||
case kDataOutPhase:
|
||
|
||
SIMg->state = kEarlySwitchingPhase;
|
||
CallHALaction( kBitBucket, &SIMg->halPB, SIMg);
|
||
break;
|
||
|
||
case kMessageInPhaseNACK:
|
||
|
||
assertATN = false;
|
||
|
||
switch ( SIMg->halPB.msg[0] )
|
||
{
|
||
case kDisconnectMsg:
|
||
ioPtr->ioStat = kDisconnectedB4Command;
|
||
DisconnectIO( ioPtr->scsiDevice.targetID,
|
||
ioPtr->scsiDevice.LUN, ioPtr);
|
||
SIMg->state = kDisconnectingB4Command;
|
||
CallHALaction( kAcceptMsg, &SIMg->halPB, SIMg);
|
||
break;
|
||
|
||
case kMsgRejectMsg: // Apparently the target didn't like our identify message
|
||
HandleRejectedIdentify( ioPtr, SIMg);
|
||
break;
|
||
|
||
case kExtendedMsg:
|
||
#if 0
|
||
if (SIMg->halPB.msgInLen >= 4)
|
||
{
|
||
if (SIMg->halPB.msg[2] == 0x01) // SDTR message
|
||
{
|
||
if ((SIMg->halPB.msgInLen == 6) && (SIMg->halPB.msg[2] == 0x03))
|
||
{
|
||
SIMg->halPB.msg[4] = MAX( SIMg->HALinfo.minPeriodFactor,
|
||
SIMg->halPB.msg[4] );
|
||
|
||
SIMg->halPB.msg[5] = MIN( SIMg->HALinfo.syncRAoffset,
|
||
SIMg->halPB.msg[5] );
|
||
|
||
SIMg->syncPeriod [ioPtr->scsiDevice.targetID] = SIMg->halPB.msg[4]; // transfer period
|
||
SIMg->syncRAoffset[ioPtr->scsiDevice.targetID] = SIMg->halPB.msg[5]; // REQ/ACK offset
|
||
|
||
CallHALSyncConfig( SIMg->syncRAoffset[ioPtr->scsiDevice.targetID],
|
||
SIMg->syncPeriod [ioPtr->scsiDevice.targetID], SIMg->HALinfo.HALstaticPtr );
|
||
|
||
CallHALAssertATN( SIMg->HALinfo.HALstaticPtr);
|
||
CallHALaction( kAcceptMsg, &SIMg->halPB, SIMg);
|
||
if (SIMg->halPB.phase == kMessageOutPhase)
|
||
{
|
||
SIMg->state = kAcceptingEarlySDTR;
|
||
SIMg->halPB.msgOutLen = 6;
|
||
CallHALaction( kSendMsgOut, &SIMg->halPB, SIMg);
|
||
}
|
||
else // no msg out phase - target async
|
||
{
|
||
SIMg->state = kEarlySwitchingPhase;
|
||
SIMg->syncRAoffset[ioPtr->scsiDevice.targetID] = 0; // asynchronous
|
||
}
|
||
break;
|
||
}
|
||
else // something wrong with format of SDTR
|
||
{
|
||
SIMg->syncRAoffset[ioPtr->scsiDevice.targetID] = 0; // asynchronous
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
// extended message that we don't care about
|
||
SIMg->state = kRejectingEarlyMsg;
|
||
CallHALAssertATN( SIMg->HALinfo.HALstaticPtr);
|
||
CallHALaction( kAcceptMsg, &SIMg->halPB, SIMg);
|
||
break;
|
||
|
||
default:
|
||
CallHALAssertATN( SIMg->HALinfo.HALstaticPtr);
|
||
CallHALaction( kAcceptMsg, &SIMg->halPB, SIMg);
|
||
}
|
||
break;
|
||
|
||
case kStatusPhase:
|
||
|
||
SIMg->state = kGettingStatus;
|
||
CallHALaction( kStatus, &SIMg->halPB, SIMg);
|
||
break;
|
||
|
||
case kMessageOutPhase: // we should only be here because we asserted ATN
|
||
|
||
SIMg->state = kSendingEarlyKillMsg;
|
||
|
||
if( !ioPtr->pendingMsgPB ) {
|
||
if( ioPtr->ioEvent & kmBadParity )
|
||
SIMg->halPB.msg[0] = kInitiatorDetectedErrorMsg;
|
||
else
|
||
SIMg->halPB.msg[0] = kNoOperationMsg; // NOP since we don't know what is going on!
|
||
}
|
||
else {
|
||
switch( ((SCSI_PB *)(ioPtr->pendingMsgPB))->scsiFunctionCode) {
|
||
|
||
case SCSITerminateIO:
|
||
SIMg->halPB.msg[0] = kTerminateIOProcessMsg;
|
||
ioPtr->ioEvent |= (kmTerminated | kmMsgSent);
|
||
break;
|
||
case SCSIAbortCommand:
|
||
SIMg->halPB.msg[0] = kAbortMsg;
|
||
ioPtr->ioEvent |= (kmAborted | kmMsgSent);
|
||
break;
|
||
case SCSIResetDevice:
|
||
SIMg->halPB.msg[0] = kBusDeviceResetMsg;
|
||
ioPtr->ioEvent |= (kmBDRSent | kmMsgSent);
|
||
break;
|
||
}
|
||
}
|
||
SIMg->halPB.msgOutLen = 1;
|
||
|
||
CallHALaction( kSendMsgOut, &SIMg->halPB, SIMg);
|
||
break;
|
||
|
||
case kCommandPhase:
|
||
|
||
CallHALaction( kCommand, &SIMg->halPB, SIMg);
|
||
SIMg->state = kSendingCommand;
|
||
break;
|
||
|
||
case kBusFreePhase:
|
||
|
||
SIMg->state = kWentBusFree;
|
||
break;
|
||
|
||
default:
|
||
|
||
IfDebugStr("\pUnknown phase in EarlySwitchPhase");
|
||
SysError( dsIOCoreErr);
|
||
}
|
||
|
||
return;
|
||
|
||
|
||
}
|
||
|
||
|
||
/*********************************************************************************
|
||
WastePhase - we're really confused, do what we need to to clear the bus
|
||
*********************************************************************************/
|
||
|
||
void
|
||
WastePhase( SIM_IO * ioPtr, SIMglobals * SIMg)
|
||
{
|
||
IfRecordEvent( (long)'DUMP', (long)'Err!');
|
||
SIMg->state = kWastingPhase;
|
||
|
||
switch (SIMg->halPB.phase) {
|
||
|
||
case kDataInPhase:
|
||
case kDataOutPhase:
|
||
|
||
CallHALaction( kBitBucket, &SIMg->halPB, SIMg);
|
||
break;
|
||
|
||
case kMessageInPhaseNACK:
|
||
|
||
CallHALAssertATN( SIMg->HALinfo.HALstaticPtr);
|
||
CallHALaction( kAcceptMsg, &SIMg->halPB, SIMg);
|
||
break;
|
||
|
||
case kStatusPhase:
|
||
|
||
SIMg->state = kGettingStatus;
|
||
CallHALaction( kStatus, &SIMg->halPB, SIMg);
|
||
break;
|
||
|
||
case kMessageOutPhase: // we should only be here because we asserted ATN
|
||
|
||
SIMg->state = kSendingEarlyKillMsg;
|
||
|
||
if( !ioPtr->pendingMsgPB ) {
|
||
if( ioPtr->ioEvent & kmBadParity )
|
||
SIMg->halPB.msg[0] = kInitiatorDetectedErrorMsg;
|
||
else
|
||
SIMg->halPB.msg[0] = kNoOperationMsg; // NOP since we don't know what is going on!
|
||
}
|
||
else {
|
||
switch( ((SCSI_PB *)(ioPtr->pendingMsgPB))->scsiFunctionCode) {
|
||
|
||
case SCSITerminateIO:
|
||
SIMg->halPB.msg[0] = kTerminateIOProcessMsg;
|
||
ioPtr->ioEvent |= (kmTerminated | kmMsgSent);
|
||
break;
|
||
case SCSIAbortCommand:
|
||
SIMg->halPB.msg[0] = kAbortMsg;
|
||
ioPtr->ioEvent |= (kmAborted | kmMsgSent);
|
||
break;
|
||
case SCSIResetDevice:
|
||
SIMg->halPB.msg[0] = kBusDeviceResetMsg;
|
||
ioPtr->ioEvent |= (kmBDRSent | kmMsgSent);
|
||
break;
|
||
}
|
||
}
|
||
SIMg->halPB.msgOutLen = 1;
|
||
|
||
CallHALaction( kSendMsgOut, &SIMg->halPB, SIMg);
|
||
break;
|
||
|
||
case kCommandPhase:
|
||
|
||
CallHALaction( kCommand, &SIMg->halPB, SIMg);
|
||
SIMg->state = kSendingCommand;
|
||
break;
|
||
|
||
case kBusFreePhase:
|
||
|
||
SIMg->state = kWastedToBusFree;
|
||
break;
|
||
|
||
default:
|
||
|
||
IfDebugStr("\pUnknown phase in WastePhase");
|
||
SysError( dsIOCoreErr);
|
||
}
|
||
|
||
return;
|
||
|
||
|
||
}
|
||
|
||
|
||
/*********************************************************************************
|
||
HandleInitWErr -
|
||
*********************************************************************************/
|
||
|
||
void
|
||
HandleInitWErr( SIM_IO * ioPtr, SIMglobals * SIMg)
|
||
{
|
||
OSErr result;
|
||
|
||
result = SIMg->halPB.result;
|
||
|
||
IfRecordEvent( (long)( (SIMg->halPB.sendCDB<<24) + (SIMg->halPB.phase<<16) + (SIMg->halPB.msgInLen<<8) + SIMg->halPB.msgOutLen),
|
||
(long)'IErr');
|
||
|
||
IfRecordEvent( (long)( (result<<16) + SIMg->halPB.action),
|
||
(long)'Err!');
|
||
|
||
if (result == kHALreselected)
|
||
{
|
||
ioPtr->ioStat = kPBidle; // go back to Idle for this one
|
||
Reconnect( SIMg);
|
||
}
|
||
else if (result == kHALpartialMsgOut)
|
||
{
|
||
if (SIMg->halPB.msgOutLen == 0) {
|
||
HandleNoIdentifyMsg( ioPtr, SIMg);
|
||
}
|
||
else if (SIMg->halPB.msgOutLen == 1) {
|
||
if ((SIMg->halPB.phase == kMessageInPhaseNACK) && (SIMg->halPB.msg[0] == kMsgRejectMsg)) {
|
||
HandleRejectedIdentify( ioPtr, SIMg);
|
||
}
|
||
else
|
||
EarlySwitchPhase( ioPtr, SIMg);
|
||
}
|
||
else // 2 or more msg bytes went out
|
||
{
|
||
if (SIMg->state == kInitiatingSDTR) {
|
||
SIMg->syncRAoffset[ioPtr->scsiDevice.targetID] = 0;
|
||
}
|
||
else { // kInitiatingAsyncMsg
|
||
if ((SIMg->halPB.phase == kMessageInPhaseNACK) && (SIMg->halPB.msg[0] == kMsgRejectMsg)) {
|
||
ioPtr->firstError = scsiKillMsgRejected;
|
||
}
|
||
}
|
||
EarlySwitchPhase( ioPtr, SIMg);
|
||
}
|
||
}
|
||
else if (result == kHALnoCommand)
|
||
{
|
||
EarlySwitchPhase( ioPtr, SIMg);
|
||
}
|
||
else if (result == kHALpartialCommand)
|
||
{
|
||
ioPtr->ioStat = kSentCommand; // pretend like we got no error
|
||
SwitchPhase( ioPtr, SIMg);
|
||
}
|
||
else if (result == kHALselectedAsTarget) // we got selected
|
||
{
|
||
SIMg->state = kHandlingSelected;
|
||
SIMg->pushedState = kIdle; // retry this IO initiation
|
||
}
|
||
else if (result == kHALselectedAsTargetFld)
|
||
{
|
||
SIMg->state = kIdle; // retry this IO initiation
|
||
}
|
||
else if (result == kHALselectFld)
|
||
{
|
||
SIMg->state = kFailingSelect;
|
||
}
|
||
else if (result == scsiUnexpectedBusFree)
|
||
{
|
||
SIMg->state = kWentBusFree;
|
||
}
|
||
else
|
||
{
|
||
IfDebugStr("\pUnknown result in HandleInitWErr");
|
||
SysError( dsIOCoreErr);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/*********************************************************************************
|
||
HandleRejectedIdentify -
|
||
*********************************************************************************/
|
||
|
||
void
|
||
HandleRejectedIdentify( SIM_IO * ioPtr, SIMglobals * SIMg)
|
||
{
|
||
#pragma unused (ioPtr)
|
||
CallHALAssertATN( SIMg->HALinfo.HALstaticPtr);
|
||
CallHALaction( kAcceptMsg, &SIMg->halPB, SIMg);
|
||
ioPtr->firstError = scsiIdentifyMessageRejected;
|
||
SIMg->state = kAcceptingRejectedIdentify;
|
||
}
|
||
|
||
|
||
/*********************************************************************************
|
||
HandleEarlyMsgIn -
|
||
*********************************************************************************/
|
||
|
||
void
|
||
HandleEarlyMsgIn( SIM_IO * ioPtr, SIMglobals * SIMg)
|
||
{
|
||
EarlySwitchPhase( ioPtr, SIMg);
|
||
}
|
||
|
||
|
||
/*********************************************************************************
|
||
HandleNoIdentifyMsg -
|
||
*********************************************************************************/
|
||
|
||
void
|
||
HandleNoIdentifyMsg( SIM_IO * ioPtr, SIMglobals * SIMg)
|
||
{
|
||
if (SIMg->halPB.phase == kCommandPhase)
|
||
EarlySwitchPhase( ioPtr, SIMg);
|
||
else // if (SIMg->halPB.phase == kMessageInPhaseNACK)
|
||
EarlySwitchPhase( ioPtr, SIMg);
|
||
}
|
||
|
||
|
||
/*********************************************************************************
|
||
SwitchPhase -
|
||
*********************************************************************************/
|
||
|
||
void
|
||
SwitchPhase( SIM_IO * ioPtr, SIMglobals * SIMg)
|
||
{
|
||
Boolean assertATN;
|
||
|
||
IfRecordEvent( (long)(SIMg->halPB.phase), (long)'SwPz');
|
||
|
||
switch (SIMg->halPB.phase) {
|
||
|
||
case kDataInPhase:
|
||
|
||
if ( ioPtr->scsiDataResidual <= 0 || ( ioPtr->SIMprivFlags & kmDataDone) ) {
|
||
ioPtr->SIMprivFlags |= kmBitBucketed;
|
||
if ( ioPtr->scsiIOFlags & scsiNoBucketIn) {
|
||
ioPtr->scsiResultFlags |= scsiBusNotFree;
|
||
CompleteIO( scsiDataRunError, ioPtr, SIMg);
|
||
}
|
||
else {
|
||
SIMg->state = kSwitchingPhase;
|
||
CallHALaction( kBitBucket, &SIMg->halPB, SIMg);
|
||
}
|
||
break;
|
||
}
|
||
|
||
SIMg->state = kDoingData;
|
||
CallHALaction( kDataIn, &SIMg->halPB, SIMg);
|
||
break;
|
||
|
||
case kDataOutPhase:
|
||
|
||
if ( ioPtr->scsiDataResidual <= 0 || ( ioPtr->SIMprivFlags & kmDataDone) ) {
|
||
ioPtr->SIMprivFlags |= kmBitBucketed;
|
||
if ( ioPtr->scsiIOFlags & scsiNoBucketOut) {
|
||
ioPtr->scsiResultFlags |= scsiBusNotFree;
|
||
CompleteIO( scsiDataRunError, ioPtr, SIMg);
|
||
}
|
||
else {
|
||
SIMg->state = kSwitchingPhase;
|
||
CallHALaction( kBitBucket, &SIMg->halPB, SIMg);
|
||
}
|
||
break;
|
||
}
|
||
|
||
SIMg->state = kDoingData;
|
||
CallHALaction( kDataOut, &SIMg->halPB, SIMg);
|
||
break;
|
||
|
||
|
||
case kMessageInPhaseNACK:
|
||
|
||
assertATN = false;
|
||
|
||
switch ( SIMg->halPB.msg[0] )
|
||
{
|
||
case kSaveDataPointerMsg: // TAKEN CARE OF IN HAL NOW
|
||
CallHALaction( kSaveDataPointer, &SIMg->halPB, SIMg); // is this perSCSI2? (or after accept)
|
||
SIMg->state = kSwitchingPhase;
|
||
break;
|
||
|
||
case kRestorePointersMsg: // TAKEN CARE OF IN HAL NOW
|
||
CallHALaction( kRestorePointers, &SIMg->halPB, SIMg); // is this perSCSI2? (or after accept)
|
||
SIMg->state = kSwitchingPhase;
|
||
break;
|
||
|
||
case kDisconnectMsg:
|
||
ioPtr->ioStat = kDisconnected;
|
||
|
||
// if SavePtrOnDisc flag is set, we should do that now
|
||
if (ioPtr->scsiIOFlags & scsiSavePtrOnDisconnect)
|
||
CallHALaction( kSaveDataPointer, &SIMg->halPB, SIMg);
|
||
|
||
SIMg->state = kDisconnecting;
|
||
DisconnectIO( ioPtr->scsiDevice.targetID,
|
||
ioPtr->scsiDevice.LUN, ioPtr);
|
||
break;
|
||
|
||
case kMsgRejectMsg: // Apparently the target didn't like our message
|
||
((SCSIHdr *)((SIM_IO *)ioPtr->pendingMsgPB))->scsiResult = scsiMessageRejectReceived;
|
||
IfRecordEvent( (long)ioPtr->pendingMsgPB, (long)'RejC');
|
||
CallCompRoutine( (SCSI_IO *)ioPtr->pendingMsgPB);
|
||
ioPtr->ioEvent |= kmMsgRejected;
|
||
break;
|
||
|
||
case kCmdCompleteMsg:
|
||
break;
|
||
|
||
case kExtendedMsg:
|
||
#if 0
|
||
if (SIMg->halPB.msgInLen >= 4)
|
||
{
|
||
if (SIMg->halPB.msg[2] == 0x01) // SDTR message
|
||
{
|
||
if ((SIMg->halPB.msgInLen == 6) && (SIMg->halPB.msg[2] == 0x03))
|
||
{
|
||
SIMg->halPB.msg[4] = MAX( SIMg->HALinfo.minPeriodFactor,
|
||
SIMg->halPB.msg[4] );
|
||
|
||
SIMg->halPB.msg[5] = MIN( SIMg->HALinfo.syncRAoffset,
|
||
SIMg->halPB.msg[5] );
|
||
|
||
SIMg->syncPeriod [ioPtr->scsiDevice.targetID] = SIMg->halPB.msg[4]; // transfer period
|
||
SIMg->syncRAoffset[ioPtr->scsiDevice.targetID] = SIMg->halPB.msg[5]; // REQ/ACK offset
|
||
|
||
CallHALSyncConfig( SIMg->syncRAoffset[ioPtr->scsiDevice.targetID],
|
||
SIMg->syncPeriod [ioPtr->scsiDevice.targetID], SIMg->HALinfo.HALstaticPtr );
|
||
|
||
CallHALAssertATN( SIMg->HALinfo.HALstaticPtr);
|
||
CallHALaction( kAcceptMsg, &SIMg->halPB, SIMg);
|
||
if (SIMg->halPB.phase == kMessageOutPhase)
|
||
{
|
||
SIMg->state = kAcceptingSDTR;
|
||
SIMg->halPB.msgOutLen = 6;
|
||
CallHALaction( kSendMsgOut, &SIMg->halPB, SIMg);
|
||
}
|
||
else // no msg out phase - target async
|
||
{
|
||
SIMg->state = kSwitchingPhase;
|
||
SIMg->syncRAoffset[ioPtr->scsiDevice.targetID] = 0; // asynchronous
|
||
}
|
||
break;
|
||
}
|
||
else // something wrong with format of SDTR
|
||
{
|
||
SIMg->syncRAoffset[ioPtr->scsiDevice.targetID] = 0; // asynchronous
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
// extended message that we don't care about
|
||
SIMg->state = kRejectingDataMsg;
|
||
CallHALAssertATN( SIMg->HALinfo.HALstaticPtr);
|
||
CallHALaction( kAcceptMsg, &SIMg->halPB, SIMg);
|
||
break;
|
||
|
||
default:
|
||
SIMg->state = kRejectingDataMsg;
|
||
assertATN = true;
|
||
}
|
||
|
||
if (!assertATN)
|
||
{
|
||
CallHALaction( kAcceptMsg, &SIMg->halPB, SIMg);
|
||
}
|
||
else
|
||
{
|
||
CallHALAssertATN( SIMg->HALinfo.HALstaticPtr);
|
||
CallHALaction( kAcceptMsg, &SIMg->halPB, SIMg);
|
||
}
|
||
break;
|
||
|
||
case kStatusPhase:
|
||
|
||
SIMg->state = kGettingStatus;
|
||
CallHALaction( kStatus, &SIMg->halPB, SIMg);
|
||
break;
|
||
|
||
case kMessageOutPhase: // we should only be here because we (or c96) asserted ATN
|
||
|
||
if( !ioPtr->pendingMsgPB ) {
|
||
if( ioPtr->ioEvent & kmBadParity ) { // did c96 assert ATN for parity error?
|
||
SIMg->halPB.msg[0] = kInitiatorDetectedErrorMsg;
|
||
SIMg->state = kSendingDetectedErrorMsg;
|
||
}
|
||
else
|
||
SIMg->halPB.msg[0] = kNoOperationMsg; // NOP since we don't know what is going on!
|
||
}
|
||
else {
|
||
SIMg->state = kSendingKillMsg;
|
||
|
||
switch( ((SCSI_PB *)(ioPtr->pendingMsgPB))->scsiFunctionCode) { // <SM7>
|
||
|
||
case SCSITerminateIO:
|
||
SIMg->halPB.msg[0] = kTerminateIOProcessMsg;
|
||
break;
|
||
case SCSIAbortCommand:
|
||
SIMg->halPB.msg[0] = kAbortMsg;
|
||
break;
|
||
case SCSIResetDevice:
|
||
SIMg->halPB.msg[0] = kBusDeviceResetMsg;
|
||
break;
|
||
} // <SM7>
|
||
}
|
||
SIMg->halPB.msgOutLen = 1;
|
||
|
||
CallHALaction( kSendMsgOut, &SIMg->halPB, SIMg);
|
||
break;
|
||
|
||
case kCommandPhase:
|
||
|
||
ioPtr->scsiResult = scsiSequenceFailed;
|
||
SIMg->state = kWastingPhase;
|
||
CallHALaction( kBitBucket, &SIMg->halPB, SIMg);
|
||
break;
|
||
|
||
case kBusFreePhase:
|
||
|
||
/* Note that this section no longer calls FreeMachine and StartMachine!
|
||
BusFreePhase during a Switch Phase is an error or the result of an abort
|
||
type message only! We shouldn't get here during a normal IO <SM3>
|
||
*/
|
||
SIMg->state = kWentBusFree;
|
||
break;
|
||
|
||
default:
|
||
|
||
IfDebugStr("\pUnknown phase in SwitchPhase");
|
||
SysError( dsIOCoreErr);
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|