mirror of
https://github.com/elliotnunn/mac-rom.git
synced 2024-12-28 01:29:20 +00:00
4325cdcc78
Resource forks are included only for .rsrc files. These are DeRezzed into their data fork. 'ckid' resources, from the Projector VCS, are not included. The Tools directory, containing mostly junk, is also excluded.
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;
|
|
}
|
|
|
|
|