sys7.1-doc-wip/OS/SCSIMgr4pt3/SIMmachine.c
2019-07-27 22:37:48 +08:00

1626 lines
44 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
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/Dont flag to make sure
if (SIMg->defaultDisc) {
if ( ioPtr->scsiFlags & scsiDontDisconnect) // if default=Do but client says Dont
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=Dont 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;
}