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

1206 lines
37 KiB
C
Raw 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: SIMCore.c
Contains: routines that implement the core of the Apple SIM layer
Entry points:
XPT Calls to SIM
void InitSIM( Ptr SIMstaticPtr, long busID)
Called by the XPT to initialize the SIMs state. The SIM, in turn has the
responsibility of optionally initializing the HBA.
void SIMAction(SIM_IO *thePB, Ptr SIMstaticPtr)
Called by the XPT whenever a SCSIAction call is received that needs to be
serviced by the SIM. The SIMstaticPtr parameter is a pointer to the previously
allocated SIM static data area.
HAL Calls to SIM
ushort SIMRegisterHAL (long HALentry, ulong staticSize, Ptr * HALstaticPtr)
Called by a Hardware Abstraction Layer (HAL) to register itself (i.e. its bus)
with the Apple standard SIM. The SIM will in turn, register this new HBA with
the XPT and will return the BusID to the HAL.
void SIMDeregisterHAL (ushort busID)
Called by a Hardware Abstraction Layer to deregister itself from the Apple
standard SIM. The SIM will in turn, deregister this HBA with the XPT.
Written by: Paul Wolf
Copyright: © 1992-1994 by Apple Computer, Inc., all rights reserved.
Change History (most recent first):
<SM29> 1/31/94 DCB Added a call to SIMDualIntPoll for the Old API case. Also
changed SIMDualIntPoll so that it will always wait for 2
consecutive polls (1 on each bus) without an interrupt before
exiting.
<SM28> 1/29/94 DCB Added SIMDualIntPoll which is called when we exit the SIMAction
call. This ensures that we don't forget about an interrupt that
might have been cleared at the VIA for the other bus.
<SM27> 12/21/93 DCB If CompAutoSense() detects that senseWaiting is non-zero and
that no PBs are awaiting autosense it needs to clear sensePBBusy
as well as senseWaiting. Otherwise we hang the next time
autosense is used.
<SM26> 11/21/93 pdw Added support for dynamic supported-flags for scFlags, scIOFlags
and scDataTypes.
<SM25> 10/14/93 pdw <MC> roll-in.
<MC4> 10/12/93 pdw Added support for Synchronous data transfers, rewrote State
Machine, message handling etc.
<MC3> 9/26/93 pdw <SM24>
<SM24> 9/16/93 DCB Fixing ResetDevice so it doesn't validate a bogus parameter
block and then return from the procedure with interrupts
blocked.
<SM23> 9/13/93 pdw Changed the risky slash pattern.
<SM22> 9/12/93 pdw Added Reregister support. Split out common registration stuff
into separate routine.
<SM21> 7/17/93 pdw Lots of little things.
<SM20> 7/8/93 pdw Added calls to RecordError.
<SM19> 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.
<SM18> 5/25/93 DCB Rollin from Ludwig. (The next item below)
<LW13> 5/21/93 PW Adding SIMg parameter to ValidateDeviceIdent so that it can
check HostID.
<SM17> 5/5/93 PW Converted names to meanies-friendly names. Updated with latest
from Ludwig stuff.
<LW12> 5/1/93 PW Changed one last name: SCSI_RegisterWithNewXPT.
<LW11> 4/30/93 DCB Added call to CallBusInquiry function so that we don't have to
busy the HALAction routine.
<LW9> 4/14/93 DCB Added RegisterWithNewXPT action for the purpose of switching
XPTs on the fly.
<LW8> 3/26/93 PW Moved VMDisableUserCode and Enable into SIMaction (from XPT).
<SM15> 3/20/93 PW Made ReleaseQ return noErr (bug introduced by removing
finalStatus from CallCompRoutine).
<LW6> 3/3/93 PW Changed default disconnect action to false (don't disconnect).
<LW5> 3/1/93 DCB Fixed ResetDevice to match ResetBus. Also changed the parameters
to callcomproutine.
<LW4> 2/18/93 DCB Changed Reset's behavior to match the documentation. Only those
IOs actually started are dequeued after a reset.
<LW3> 1/27/93 PW Added support for register->SIMinit->HALinit way of
initialization. Added SIMInterruptPoll routine.
<LW2> 12/17/92 DCB Fixed a bug in the reset device,abort command and terminateIO
features which caused a sync-wait hang.
<SM13> 12/9/92 DCB Fixed SyncWait problem for synchronous Reset Bus commands by
returning a noErr after doing the reset.
<SM12> 12/5/92 PW Changed ValidateDI to return the appropriate error.
<SM11> 11/20/92 DCB Added support for asynchronous abort, terminate, reset device
and other functions
<SM10> 11/1/92 DCB Removed bogus validate check before doing BusInquiry.
<SM9> 10/30/92 DCB Changed to reflect new ACAM.h
<SM8> 10/8/92 PW Added stuff needed for variable-sized SCSI_IO support and for
new ioEvent field.
<SM7> 9/17/92 PW Added check for function code not supported.
<SM6> 9/14/92 DCB Added some parameter block validation
<SM5> 9/11/92 DCB Fixing Terminate and Abort IO and adding Reset Device
<SM4> 8/31/92 DCB Implementing Terminate/Abort IO
<SM3> 8/20/92 DCB Fixed SCSI Reset
<SM2> 8/1/92 PW Added comments, function headers and prototypes.
*/
#include <Types.h>
#include <Memory.h>
#include <Errors.h>
#include <SCSIStandard.h>
#include <SCSI.h>
#include "ACAM.h"
#include "XPT.h"
#include "CUtils.h"
#include "SIMCore.h"
#include "SIMCorePriv.h"
#include "SIMQ.h"
#include "SCSIGlue.h"
#include "HALc96.h"
#include "SCSIDebug.h"
#include "Recorder.h"
//———— Prototypes for functions used only within this file ————
void ResetDevice( SCSIResetDevicePB *resetPtr, SIMglobals * SIMg);
void TerminateIO( SCSITerminateIOPB *termPtr, SIMglobals * SIMg);
void AbortCommand( SCSIAbortCommandPB *abortPtr, SIMglobals * SIMg);
void SIMaction( SIM_IO *ioPtr, SIMglobals * SIMg);
Boolean StartMachine( SIMglobals * SIMg);
void SetupSensePB( SIM_IO * ioPtr, SIMglobals * SIMg );
Boolean CheckErrors( OSErr finalStatus, SIM_IO * ioPtr, SIMglobals * SIMg );
pascal void CompAutoSense( SIM_IO *ioPtr );
pascal void CompResetDevice( SIM_IO *ioPtr );
void FreeMachine( OSErr finalStatus, SIM_IO * ioPtr, SIMglobals * SIMg);
SIMinitInfo * SIMRegisterCommon( HALinitInfo *HALinfoPtr);
/*********************************************************************************
SIMRegisterHAL - register a HAL for the SIMCore
*********************************************************************************/
OSErr
SIMRegisterHAL( HALinitInfo *HALinfoPtr)
{
SIMinitInfo * SIMinfoPtr;
// Fill in our entry points
SIMinfoPtr = SIMRegisterCommon( HALinfoPtr);
// Make call ———————————————— (which in turn calls SIMinit)
return ( SCSIRegisterBus( SIMinfoPtr) );
}
/*********************************************************************************
SIMRegisterHAL - register a HAL for the SIMCore
*********************************************************************************/
OSErr
SIMReregisterHAL( HALinitInfo *HALinfoPtr)
{
OSErr err;
SIMinitInfo * SIMinfoPtr;
// Fill in our entry points
SIMinfoPtr = SIMRegisterCommon( HALinfoPtr);
// Make call ———————————————— (which doesn't call SIMinit)
SIMinfoPtr->busID = HALinfoPtr->busID;
err = SCSIReRegisterBus( SIMinfoPtr);
if (err) return(err);
return ( SIMinit( SIMinfoPtr));
}
/*********************************************************************************
SIMRegisterCommon -
*********************************************************************************/
SIMinitInfo *
SIMRegisterCommon( HALinitInfo *HALinfoPtr)
{
SIMinitInfo * SIMinfoPtr;
// Set up SIMinfoPtr for call to SCSIRegisterBus
if( 0==(SIMinfoPtr = (SIMinitInfo *) NewPtrSysClear(sizeof(SIMinitInfo)))) // We need to keep this around for re-registration
return (SIMinfoPtr);
// -> num bytes SIM needs for static vars
SIMinfoPtr->staticSize = sizeofSIMGlobals + HALinfoPtr->staticSize;
SIMinfoPtr->SIMinit = SIMinit; // -> SIM init routine
SIMinfoPtr->SIMaction = SIMaction; // -> SIM action routine
SIMinfoPtr->NewOldCall = NewOldCall; // -> SIM NewOldCall routine
SIMinfoPtr->SIM_ISR = SIM_ISR; // -> SIM int-> service routine
SIMinfoPtr->SIMInterruptPoll = SIMDualIntPoll; // -> SIM int-> poll routine
SIMinfoPtr->ioPBSize = sizeof(SIM_IO); // -> size of IO PB needed
SIMinfoPtr->oldCallCapable = HALinfoPtr->oldCallCapable; // -> capable of old calls
SIMinfoPtr->simInternalUse = (long)HALinfoPtr; // -> so SIMinit knows
return (SIMinfoPtr);
}
/*********************************************************************************
SIMinit -
*********************************************************************************/
OSErr
SIMinit (SIMinitInfo * SIMinfoPtr)
{
SIMglobals * SIMg;
HALinitInfo * HALinfoPtr;
OSErr err;
//--- Get results out of SIMinfo and put in SIMglobals and in HALInfo
// <- get G ptr from ptr allocated by XPT and copy entire SIMinfo over there
SIMg = (SIMglobals *)SIMinfoPtr->SIMstaticPtr;
HALinfoPtr = (HALinitInfo *)SIMinfoPtr->simInternalUse;
// give HAL its staticPtr which should point just past SIM's globals
HALinfoPtr->HALstaticPtr = (uchar *)SIMg + sizeofSIMGlobals;
// give HAL the SIMCore's global ptr also, for callbacks to SSM
HALinfoPtr->SIMstaticPtr = (uchar *)SIMg;
// and the addresses for callbacks to SSM
HALinfoPtr->ReconnectISR = ReconnectISR;
// The XPT gives the SIM its ISR so that it can install it - give it to the HAL instead
HALinfoPtr->XPT_ISR = SIMinfoPtr->XPT_ISR;
// <- busID returned - store it
HALinfoPtr->busID = SIMinfoPtr->busID;
// Store all of the HALinfo for later reference
SIMg->HALinfo = *HALinfoPtr;
SIMg->MakeCallback = SIMinfoPtr->MakeCallback;
//--- Set up various state things (that shouldn't be init'd at zero)
SIMg->state = kIdle;
SIMg->defaultDisc = false;
SIMg->initInfo = SIMinfoPtr; // Save this for re-registration
StartTimer( SIMg );
//--- Make call to HALinit then get what we need from HAL
if( err=(HALinfoPtr->HALinit)(HALinfoPtr) )
return (err);
SIMg->hostID = GetInitiatorID( (HBADesc_53c9x *)(HALinfoPtr->HALstaticPtr));
//--- Initialize fields in HALActionPB that will never change
SIMg->halPB.HALstaticPtr = SIMg->HALinfo.HALstaticPtr;
return ( noErr);
}
/*********************************************************************************
SIMaction - The main SIMCore entry point
*********************************************************************************/
void
SIMaction( SIM_IO *ioPtr, SIMglobals * SIMg)
{
VMDisableUserCode(); // disable user code (so page faults won't happen)
// What do we need to do?
// I moved this here because ALL of these routines are now considered to be
// asynchronous - not just those that are enqueued into the IOQ. <SM11> DCB
ioPtr->scsiResult = scsiRequestInProgress;
if (ioPtr->scsiFunctionCode == SCSIExecIO || ioPtr->scsiFunctionCode == SCSIOldCall)
{
if( ValidatePB(ioPtr, SIMg)) {
EnQIO( ioPtr, SIMg);
NewIOStartMachine( SIMg);
}
}
else
{
switch (ioPtr->scsiFunctionCode)
{
case SCSIReleaseQ:
if( ioPtr->scsiResult = ValidateDeviceIdent( ioPtr->scsiDevice, SIMg)) {
CallCompRoutine( (SCSI_IO *)ioPtr);
}
else {
DefrostLUN( ioPtr->scsiDevice.targetID, ioPtr->scsiDevice.LUN );
NewIOStartMachine( SIMg);
ioPtr->scsiResult = noErr;
CallCompRoutine( (SCSI_IO *) ioPtr);
}
break;
case SCSIBusInquiry: // SIM
((SCSIBusInquiryPB *)ioPtr)->scsiSIMPrivatesPtr = SIMg;
((SCSIBusInquiryPB *)ioPtr)->scsiSIMPrivatesSize = sizeofSIMGlobals;
((SCSIBusInquiryPB *)ioPtr)->scsiIOpbSize = sizeof(SIM_IO);
CallBusInquiry( ioPtr, SIMg);
ioPtr->scsiResult = noErr;
CallCompRoutine( (SCSI_IO *) ioPtr);
break;
case SCSIResetBus: // SIM
ResetSIM( SIMg );
ioPtr->scsiResult = noErr;
IfRecordEvent( (long)ioPtr, (long)'RsOC');
CallCompRoutine( (SCSI_IO *)ioPtr); // <SM13>
break;
case SCSIResetDevice: // SIM
ResetDevice( (SCSIResetDevicePB *) ioPtr, SIMg);
break;
case SCSIAbortCommand: // SIM
AbortCommand( (SCSIAbortCommandPB *) ioPtr, SIMg);
break;
case SCSITerminateIO: // SIM
TerminateIO( (SCSITerminateIOPB *) ioPtr, SIMg);
break;
case SCSIRegisterWithNewXPT:
ioPtr->scsiResult = SCSIReRegisterBus( SIMg->initInfo );
break;
default:
ioPtr->scsiResult = scsiFunctionNotAvailable;
IfRecordError( (long)'FC?C');
IfRecordEvent( (long)ioPtr, (long)ioPtr->scsiFunctionCode);
CallCompRoutine( (SCSI_IO *)ioPtr);
break;
}
}
// If this was a Quadra 900 then we want to check to see if any interrupts
// for the other bus have occured and been cleared...
if( SIMg->otherSIMg )
SIMDualIntPoll(SIMg);
VMEnableUserCode(); // enable user code (allow page faults)
}
/*********************************************************************************
NewOldCall -
*********************************************************************************/
void
NewOldCall( SIM_IO * ioPtr, SIMglobals * SIMg)
{
VMDisableUserCode(); // disable user code (so page faults won't happen)
ioPtr->scsiResult = scsiRequestInProgress;
ioPtr->scsiOldCallResult = scsiRequestInProgress;
CallMachine( SIMg);
// If this was a Quadra 900 then we want to check to see if any interrupts
// for the other bus have occured and been cleared...
if( SIMg->otherSIMg )
SIMDualIntPoll(SIMg);
VMEnableUserCode(); // enable user code (allow page faults)
}
/*********************************************************************************
SIM_ISR -
*********************************************************************************/
#if 0
long
SIM_ISR (SIMglobals * SIMg)
{
return (((SIMg->HALinfo).HAL_ISR)((SIMg->HALinfo).HALstaticPtr));
}
#endif
/*********************************************************************************
SIMIntPoll -
*********************************************************************************/
long
SIMIntPoll (SIMglobals * SIMg)
{
return (((SIMg->HALinfo).HALIntPoll)((SIMg->HALinfo).HALstaticPtr));
}
/*********************************************************************************
SIMDualIntPoll -
*********************************************************************************/
long
SIMDualIntPoll (SIMglobals * SIMg)
{
SIMglobals * AltSIMg;
ulong GotInt;
if( SIMg->otherSIMg == nil )
return(SIMIntPoll(SIMg));
AltSIMg = SIMg;
do {
// IfRecordEvent((long)AltSIMg, (long)'SItp');
GotInt = SIMIntPoll(AltSIMg);
AltSIMg = (SIMglobals *) AltSIMg->otherSIMg;
if( GotInt )
continue;
// IfRecordEvent((long)AltSIMg, (long)'SItp');
GotInt = SIMIntPoll(AltSIMg);
AltSIMg = (SIMglobals *) AltSIMg->otherSIMg;
} while( GotInt ); // Repeat until 2 in a row didn't handle an interrupt
return(0); // !!! nobody is paying attention to this currently
}
/*********************************************************************************
CallBusInquiry - call HAL to get info on the bus
*********************************************************************************/
Boolean // completed Synchronously
CallBusInquiry( SIM_IO * ioPtr, SIMglobals * SIMg)
{
// Call HAL and return result
return ((SIMg->HALinfo.HALBusInquiry)( SIMg->HALinfo.HALstaticPtr, (SCSIBusInquiryPB *)ioPtr ));
}
/*********************************************************************************
ResetSIM - It's the end of the world as we know it...
And I feel fine.
*********************************************************************************/
void
ResetSIM( SIMglobals * SIMg)
{
int LUN,id;
SIM_IO * qPtr;
SIMg->SIMBusy = true; // Don't start IOs until we are done.
/* Reset Bus, Most of the chip, and go bus free */
CallHALResetBus( SIMg->HALinfo.HALstaticPtr ); /* Say goodbye to Hollywood */
for( id = 0; id < 7; id+=1 ) {
for( LUN = 0; LUN < 8; LUN+=1 ) {
SIMg->discIOs[id][LUN] = 0; /* Reset should have gotten rid of connected ios */
SIMg->LUNstate[id][LUN] = 0;
}
}
SIMg->state = kIdle;
SIMg->SSMflags = 0;
SIMg->senseWaiting = 0;
/* Don't send any reset device messages <SM11> */
while( qPtr = (SIM_IO *)SIMg->resetPBQ.qHead ) { // <SM11>
qPtr->scsiResult = scsiSCSIBusReset;
IfRecordEvent( (long)qPtr, (long)'RstC');
CallCompRoutine( (SCSI_IO *) qPtr);
Dequeue( (QElemPtr) qPtr,&SIMg->resetPBQ);
}
ResetQueues( SIMg, 0xFF ); /* Move the IO Q into the Reset Q */
while( qPtr = (SIM_IO *)SIMg->resetHdr.qHead ) { /* CompleteIO will dequeue each entry <LW4> <TOP> */
if( (qPtr->scsiFlags & scsiSIMQNoFreeze) || qPtr->scsiFunctionCode == SCSIOldCall ) {
DefrostLUN( qPtr->scsiDevice.targetID, qPtr->scsiDevice.LUN);
}
else {
FreezeLUN( qPtr->scsiDevice.targetID, qPtr->scsiDevice.LUN);
qPtr->scsiResultFlags |= scsiSIMQFrozen;
}
CompleteIO( scsiSCSIBusReset, (SIM_IO *) qPtr, SIMg);
} /* <LW4> <BOTTOM> */
SIMg->SIMBusy = false; // Allow IOs again
NewIOStartMachine(SIMg); // Make sure Machine gets run
}
/*********************************************************************************
ResetDevice - Reset a particular SCSI device with a ResetDevice Message <SM5>
*********************************************************************************/
void
ResetDevice( SCSIResetDevicePB *resetPtr, SIMglobals * SIMg)
{
SIM_IO *ioPtr, *qPtr;
ushort oldSR;
Boolean usedExisting = false;
uchar targetID;
if( resetPtr->scsiResult = ValidateDeviceIdent( resetPtr->scsiDevice, SIMg)) {
IfRecordEvent( (long)resetPtr, (long)'RsDC');
CallCompRoutine( (SCSI_IO *)resetPtr);
return;
}
oldSR = BlockInterrupts(); //-> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> ->
targetID = (resetPtr->scsiDevice).targetID;
IfRecordEvent( (long)targetID, (long)'RstD');
ioPtr = FindIOFromID( targetID, SIMg);
//……… If we are already connected to the device - then we can use the existing
// connection. If not we will have to use the one in the globals.
if( ioPtr && ioPtr->ioStat != kPBidle ) {
ioPtr->pendingMsgPB = (Ptr)resetPtr; // <SM11> DCB
SwitchQ(ioPtr, &SIMg->immedQHdr);
usedExisting = true;
}
//……… Get rid of ALL other PBs for this guy that haven't been started
ResetQueues( SIMg, targetID );
//……… For each IO in resetQ, freeze the LUN's Q if it's a new-API request w/o the NoFreeze bit set
while( qPtr = (SIM_IO *)SIMg->resetHdr.qHead ) { // CompleteIO will dequeue each entry
if( (qPtr->scsiFlags & scsiSIMQNoFreeze) || qPtr->scsiFunctionCode == SCSIOldCall ) {
DefrostLUN( qPtr->scsiDevice.targetID, qPtr->scsiDevice.LUN);
}
else
{
// Do actual freezing of LUN then indicate that it is so in the SCSI_IO PB
FreezeLUN( qPtr->scsiDevice.targetID, qPtr->scsiDevice.LUN);
qPtr->scsiResultFlags |= scsiSIMQFrozen;
}
CompleteIO( scsiBDRsent, (SIM_IO *) qPtr, SIMg);
} //<LW5> <BOTTOM>
//……… ResetDevice should have gotten rid of in-progress IOs - clear them from table
if( !usedExisting ) {
if( SIMg->resetPBQ.qHead == 0 ) { /* The global Reset PB is free */
SetupResetPB( targetID , SIMg);
EnQIO(&SIMg->resetDevPB,SIMg);
SIMg->resetDevPB.pendingMsgPB = (Ptr) resetPtr; /* Remember who started this <LW2> */
}
Enqueue((QElemPtr)resetPtr, &SIMg->resetPBQ);
}
UnblockInterrupts(oldSR); //<- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <-
NewIOStartMachine(SIMg); /* Make sure Machine gets run */
}
/*********************************************************************************
TerminateIO -
*********************************************************************************/
void
TerminateIO( SCSITerminateIOPB *termPtr, SIMglobals * SIMg) // <SM4>
{
SIM_IO * ioPtr;
ushort oldSR;
Boolean restartMachine = false;
oldSR = BlockInterrupts(); //-> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> ->
ioPtr = (SIM_IO *)(termPtr->scsiIOptr);
IfRecordEvent( (long)ioPtr, (long)'TmIO');
if( FindIO( ioPtr, SIMg) == 0 ) {
termPtr->scsiResult = scsiUnableToTerminate;
IfRecordEvent( (long)termPtr, (long)'Tm1C');
CallCompRoutine( (SCSI_IO *)termPtr); /* Dang - PB not in queue <LW5> */
goto exit;
}
if( ioPtr->ioStat == kPBidle ) { /* If it hasn't been started - just de-queue it */
CompleteIO( scsiTerminated, (SIM_IO *) ioPtr, SIMg );
goto exit;
}
/* If it has been started let Machine() deal with it */
if( ioPtr->pendingMsgPB ) {
termPtr->scsiResult = scsiUnableToTerminate;
IfRecordEvent( (long)termPtr, (long)'Tm2C');
CallCompRoutine( (SCSI_IO *)termPtr);
}
ioPtr->pendingMsgPB = (Ptr)termPtr; // <SM11> DCB
restartMachine = true;
SwitchQ(ioPtr, &SIMg->immedQHdr);
exit:
UnblockInterrupts( oldSR); //<- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <-
if( restartMachine )
NewIOStartMachine( SIMg); /* Make sure Machine gets run */
}
/*********************************************************************************
AbortCommand -
*********************************************************************************/
void
AbortCommand( SCSIAbortCommandPB *abortPtr, SIMglobals * SIMg) // <SM4>
{
SIM_IO * ioPtr;
ushort oldSR;
Boolean restartMachine = false;
oldSR = BlockInterrupts(); //-> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> ->
ioPtr = (SIM_IO *)(abortPtr->scsiIOptr);
IfRecordEvent( (long)ioPtr, (long)'Abt ');
if( FindIO( ioPtr, SIMg) == 0 ) {
abortPtr->scsiResult = scsiUnableToAbort;
IfRecordEvent( (long)abortPtr, (long)'Ab1C');
CallCompRoutine( (SCSI_IO *)abortPtr); /* Dang - PB not in queue <LW2> */
goto exit;
}
if( ioPtr->ioStat == kPBidle ) { /* If it hasn't been started - just de-queue it */
CompleteIO( scsiTerminated, (SIM_IO *) ioPtr, SIMg);
goto exit;
}
/* If it has been started let Machine() deal with it */
if( ioPtr->pendingMsgPB ) {
abortPtr->scsiResult = scsiUnableToAbort;
IfRecordEvent( (long)abortPtr, (long)'Ab2C');
CallCompRoutine( (SCSI_IO *)abortPtr);
}
ioPtr->pendingMsgPB = (Ptr)abortPtr; // <SM11> DCB
restartMachine = true;
SwitchQ(ioPtr,(QHdrPtr) &SIMg->immedQHdr);
exit:
UnblockInterrupts( oldSR); // <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <-
if( restartMachine )
NewIOStartMachine( SIMg); /* Make sure Machine gets run */
}
/*********************************************************************************
ValidatePB - Makes sure the PB is "right with GOD" (Us)
Errors with precedence should appear at the bottom in case
there is more than one thing wrong with the PB
*********************************************************************************/
Boolean
ValidatePB( SIM_IO * ioPtr, SIMglobals * SIMg )
{
OSErr finalStatus = noErr;
if( ioPtr->scsiPBLength < sizeof(SIM_IO) ) {
finalStatus = scsiPBLengthError; /* Not enough room for SIM and HAL privates <SM5> */
goto FinalCheck;
}
if( ioPtr->scsiFlags & (0xffffffff - SIMg->HALinfo.supported_scFlags) ) {
finalStatus = scsiProvideFail;
goto FinalCheck;
}
if( ioPtr->scsiIOFlags & (0xffff - SIMg->HALinfo.supported_scIOFlags) ) {
finalStatus = scsiProvideFail;
goto FinalCheck;
}
if( finalStatus = ValidateDeviceIdent( ioPtr->scsiDevice, SIMg)) {
if (finalStatus == scsiTIDInvalid)
ioPtr->scsiOldCallResult = scsiSelectTimeout;
goto FinalCheck;
}
if( ioPtr->scsiCDBLength > 12 ) {
finalStatus = scsiCDBLengthInvalid; /* We can't handle CDB's longer than 12 bytes */
goto FinalCheck;
}
// if( ioPtr->scsiFunctionCode != SCSIOldCall ) {
// if( ioPtr->scsiDataType == kTib )
// finalStatus = scsiProvideFail; /* TIBs not supported */
// goto FinalCheck;
// }
if( !( (1<<(ioPtr->scsiDataType)) & SIMg->HALinfo.supported_scDataTypes) ) {
finalStatus = scsiDataTypeInvalid; /* data type not supported */
goto FinalCheck;
}
if( ioPtr->scsiTransferType > kMaxTransferType ) {
finalStatus = scsiTransferTypeInvalid; /* xfer type too high */
goto FinalCheck;
}
FinalCheck:
if( finalStatus != noErr ) {
ioPtr->scsiResult = finalStatus;
IfRecordEvent( (long)ioPtr, (long)'InvC');
CallCompRoutine( (SCSI_IO *)ioPtr); /* Whoops, not a valid PB */
return(false);
}
return(true);
}
/*********************************************************************************
ValidateDeviceIdent - Makes sure the target ID is valid
*********************************************************************************/
OSErr
ValidateDeviceIdent( DeviceIdent devIdent, SIMglobals * SIMg )
{
if( devIdent.targetID > kHighestTarget )/* Make Sure ID is in range <SM4>*/
return( scsiTIDInvalid);
if( devIdent.targetID == SIMg->hostID ) /* Can't allow Mac's id <SM4>*/
return( scsiTIDInvalid);
if( devIdent.LUN > 7 ) /* Allow all 8 LUNs */
return( scsiLUNInvalid);
return( noErr );
}
/*********************************************************************************
StartMachine - if SSM isn't already running, start machine if there
is a ready IO or R_select waiting
*********************************************************************************/
Boolean
StartMachine( SIMglobals * SIMg)
{
Boolean wasBusy;
ushort oldSR;
wasBusy = bset(kbSSMBusy, &SIMg->SSMflags);
if (wasBusy)
return( false); // didn't get the state machine this time
oldSR = BlockInterrupts(); //-> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> ->
if ( SIMg->r_selectWaiting)
{
SIMg->state = kGotR_selected;
}
else if ( 0!=( SIMg->halPB.ioPtr= GetNextReadyIO(SIMg) ))
{
SIMg->state = kIdle;
}
else
{
(void) bclr(kbSSMBusy, &SIMg->SSMflags);
UnblockInterrupts( oldSR);
return( false); // nothing to do so don't start machine
}
UnblockInterrupts( oldSR); //<- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <-
SIMg->halPB.result = noErr;
return( true); // we did get the machine and we do have something to do
}
/*********************************************************************************
NewIOStartMachine - if SSM isn't already running, start it
*********************************************************************************/
void
NewIOStartMachine(SIMglobals * SIMg)
{
IfRecordEvent( sp(), (long)'NwIO');
if (StartMachine( SIMg))
CallMachine( SIMg);
}
/*********************************************************************************
ReconnectISR -
*********************************************************************************/
void
ReconnectISR( SIMglobals * SIMg)
{
SIMg->r_selectWaiting = true;
if (StartMachine( SIMg))
CallMachine( SIMg);
}
/*********************************************************************************
FreeMachine - free up SSM so that another connection can use it
*********************************************************************************/
void
FreeMachine( OSErr finalStatus, SIM_IO * ioPtr, SIMglobals * SIMg)
{
Boolean doingAutosense;
if (ioPtr)
{
//……… this is order-critical - we should free up the machine before calling comprtn in case of a sync req
// from the comprtn
doingAutosense = CheckErrors( finalStatus, ioPtr, SIMg);
SIMg->state = kIdle;
(void) bclr(kbSSMBusy, &SIMg->SSMflags);
if( !doingAutosense )
CompleteIO( finalStatus, ioPtr, SIMg);
}
else {
SIMg->state = kIdle;
(void) bclr(kbSSMBusy, &SIMg->SSMflags);
}
if (StartMachine( SIMg))
CallMachine( SIMg);
}
/*********************************************************************************
CompleteIO -
*********************************************************************************/
void
CompleteIO( OSErr finalStatus, SIM_IO * ioPtr, SIMglobals * SIMg )
{
IfRecordEvent( (long)(ioPtr), (long)'CpIO');
DeQIO( ioPtr, SIMg ); // <SM2>
//--- if we don't yet have an error reported on this IO request,
// check for data run problem or a non-zero SCSI status
if ( finalStatus==noErr ) {
if ( ioPtr->scsiSCSIstatus!=0 ) {
finalStatus = scsiNonZeroStatus;
IfRecordError(finalStatus);
}
else if ( ioPtr->firstError) {
finalStatus = ioPtr->firstError;
IfRecordError(finalStatus);
}
else if ( ioPtr->scsiDataResidual != 0 || ( ioPtr->SIMprivFlags & kmBitBucketed) ) {
finalStatus = scsiDataRunError;
IfRecordError(finalStatus);
}
}
else {
IfRecordError(finalStatus);
}
//--- If we tried to send a message (abort, TermIO etc) we need to figure out what happened to it
// and call the completion routine for the control parameter block that was responsible for the message
// Note that even if multiple events happened we can only report one in an error message... <LW9>
// %%%!!! Move this elsewhere - we don't need to check for a parity error on every I/O
if( ioPtr->ioEvent & kmBadParity )
finalStatus = scsiParityError;
else if( ioPtr->pendingMsgPB ) {
if( ioPtr->ioEvent & kmTimedOut ) // <SM7>
finalStatus = scsiCommandTimeout;
else if( ioPtr->ioEvent & kmAborted )
finalStatus = (ioPtr->ioEvent & kmMsgRejected ) ? scsiUnableToAbort : scsiRequestAborted;
else if( ioPtr->ioEvent & kmTerminated )
finalStatus = (ioPtr->ioEvent & kmMsgRejected ) ? scsiUnableToTerminate : scsiTerminated;
else if( ioPtr->ioEvent & kmBDRSent )
finalStatus = (ioPtr->ioEvent & kmMsgRejected ) ? scsiMessageRejectReceived : scsiBDRsent;
((SCSIHdr *)((SIM_IO *)ioPtr->pendingMsgPB))->scsiResult = noErr;
IfRecordEvent( (long)ioPtr->pendingMsgPB, (long)'PndC');
CallCompRoutine( (SCSI_IO *)ioPtr->pendingMsgPB);
}
/* unlock the user's buffers if necessary <SM6> DCB */
if( (ioPtr->SIMprivFlags & (1 << kbSetupComplete)) != 0 )
{
CallHALTeardownIO( ioPtr, SIMg->HALinfo.HALstaticPtr);
}
ioPtr->scsiResult = finalStatus;
CallCompRoutine( (SCSI_IO *)ioPtr);
}
/*********************************************************************************
CallCompRoutine -
*********************************************************************************/
void
CallCompRoutine( SCSI_IO * ioPtr ) // <SM7>
{
VMEnableUserCode(); // we're safe now
XPTCallCompRoutine( ioPtr); // MAKE CALLBACK
VMDisableUserCode(); // and now we're not safe again
}
/*********************************************************************************
CheckErrors - check for errors and do queue maintenance
*********************************************************************************/
Boolean
CheckErrors( OSErr finalStatus, SIM_IO * ioPtr, SIMglobals * SIMg )
{
SIM_IO * originalPB;
ushort oldSR;
// If this was a return from an autosense ………………………………
if( ioPtr == &SIMg->sensePB ) {
originalPB = (SIM_IO *)ioPtr->scsiDriverStorage; /* We stuffed it here earlier */
if( finalStatus == noErr ) /* Request Sense succeeded */
originalPB->scsiResultFlags |= scsiAutosenseValid;
originalPB->ioEvent |= kmAutoSense; // Remember that we did autosense
CheckErrors( originalPB->savedResult, originalPB, SIMg ); /* Freeze LUN if necessary */
return(false); /* Now do CompleteIO for the Request Sense which will
do a CompleteIO for the original Parameter block
*/
}
/* Check if there was a check condition
If check condition and autosense
If the autosense PB is available
Fill in the autosense PB
Set the "Enqueue at Head" bit
use ioPB's autosense buffer
Enqueue autosense PB
else
remember that we are in line for autosense
Remember the final status for later
return; That is let the autosense's instance of CheckErrors worry about the queue
*/
// if we're being called before autosense ………………………………
if ( !(ioPtr->ioEvent & kmAutoSense) ) // Just because it isn't valid doesn't mean we didn't try...
{
// if we need to do an autosense
if ( ioPtr->scsiSCSIstatus == scsiStatCheckCondition
&& !(ioPtr->scsiFlags & scsiDisableAutosense)
&& (ioPtr->scsiSenseLength) )
{
oldSR = BlockInterrupts(); //-> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> ->
SIMg->senseWaiting+=1;
ioPtr->ioStat = kNeedsSense;
ioPtr->savedResult = finalStatus;
if ( ! SIMg->sensePBBusy ) {
SIMg->sensePBBusy = true;
SetupSensePB(ioPtr,SIMg); /* Sets the "Enqueue at head" bit */
EnQIO(&SIMg->sensePB,SIMg);
}
UnblockInterrupts( oldSR); //<- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <-
/* Return true so completeIO doesn't get called */
return(true); /* SwitchPhase is next up - should startup the autosense */
}
}
/* If (there was an error - or we were supposed to freeze the queue)
AND it is OK to freeze the queue - then freeze it.
*/
if( (finalStatus != noErr
|| ioPtr->scsiSCSIstatus != 0
|| (ioPtr->scsiFlags & scsiSIMQFreeze))
&& (ioPtr->scsiFlags & scsiSIMQNoFreeze) == 0 ) {
FreezeLUN( ioPtr->scsiDevice.targetID, ioPtr->scsiDevice.LUN);
ioPtr->scsiResultFlags |= scsiSIMQFrozen;
}
return(false);
}
/*********************************************************************************
SetupSensePB - setup the parameter block for an automatic request sense
*********************************************************************************/
void
SetupSensePB( SIM_IO * ioPtr, SIMglobals * SIMg )
{
uchar *cmd;
cmd = (uchar *)&SIMg->sensePB.scsiCDB;
SIMg->sensePB.scsiResult = scsiRequestInProgress; // EnqIO doesn't do this anymore... <SM10>
cmd[0] = 0x03; /* Request Sense */
cmd[1] = (ioPtr->scsiDevice.LUN) << 5; /* Fill in LUN */
cmd[2] = 0;
cmd[3] = 0;
cmd[4] = ioPtr->scsiSenseLength;
cmd[5] = 0;
SIMg->sensePB.scsiCDBLength = 6;
SIMg->sensePB.scsiPBLength = sizeof(SIM_IO);
SIMg->sensePB.scsiDataPtr = ioPtr->scsiSensePtr;
SIMg->sensePB.scsiDataLength = ioPtr->scsiSenseLength;
if ( (ioPtr->scsiIOFlags & scsiRenegotiateSense) && SIMg->syncRAoffset[ioPtr->scsiDevice.targetID])
SIMg->sensePB.scsiFlags = scsiInitiateSyncData;
SIMg->sensePB.scsiFlags |= scsiDirectionIn | scsiSIMQHead | /* MUST enqueue at the head!!! */
(ioPtr->scsiFlags & (
scsiDoDisconnect |
scsiDontDisconnect));
SIMg->sensePB.scsiIOFlags = scsiNoParityCheck |
(ioPtr->scsiIOFlags & (
scsiDisableSelectWAtn |
scsiSavePtrOnDisconnect));
SIMg->sensePB.scsiDevice = ioPtr->scsiDevice;
SIMg->sensePB.scsiDriverStorage = (uchar *) ioPtr;
SIMg->sensePB.scsiCompletion = CompAutoSense;
SIMg->sensePB.scsiTransferType = scsiTransferPolled;
SIMg->sensePB.scsiFunctionCode = SCSIExecIO;
}
/*********************************************************************************
SetupResetPB - setup the parameter block for a reset device <SM3>
*********************************************************************************/
void
SetupResetPB( uchar TargetID, SIMglobals * SIMg )
{
uchar *cmd;
cmd = (uchar *)&SIMg->sensePB.scsiCDB;
cmd[0] = 0x00; /* TUR - Something Safe */
cmd[1] = 0;
cmd[2] = 0;
cmd[3] = 0;
cmd[4] = 0;
cmd[5] = 0;
SIMg->resetDevPB.scsiPBLength = sizeof(SIM_IO);
SIMg->resetDevPB.scsiCDBLength = 6;
SIMg->resetDevPB.scsiFlags = scsiDirectionNone | scsiSIMQHead; /* enqueue at the head */
SIMg->resetDevPB.scsiDevice.targetID = TargetID; /* Entire device will get reset */
SIMg->resetDevPB.scsiDriverStorage = 0;
SIMg->resetDevPB.scsiCompletion = CompResetDevice; // <LW3>
SIMg->resetDevPB.scsiFunctionCode = SCSIExecIO;
}
/*********************************************************************************
CompAutoSense - autosense PB completion routine
*********************************************************************************/
pascal void
CompAutoSense( SIM_IO * senseIOPtr )
{
SIM_IO * nextSense;
SIM_IO * originalIOPtr;
SIMglobals * SIMg;
ushort oldSR;
VMDisableUserCode();
// Get stuff from autosense PB that we need for original request and get SIMg
originalIOPtr = (SIM_IO *)senseIOPtr->scsiDriverStorage;
SIMg = (SIMglobals *)senseIOPtr->SIMg;
originalIOPtr->scsiSenseResidual = senseIOPtr->scsiDataResidual;
originalIOPtr->ioStat = kGotSense; // Don't decrement senseWaiting in Dequeue
// Get the next PB which is waiting with Check Condition
oldSR = BlockInterrupts(); //-> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> ->
if ( --(SIMg->senseWaiting) > 0 ) {
nextSense = GetNextAutoSense( SIMg);
if (!nextSense) {
IfDebugStr("\psenseWaiting !=0 but none say kNeedsSense");
SIMg->senseWaiting = 0;
SIMg->sensePBBusy = 0;
}
else {
SetupSensePB( nextSense, SIMg);
EnQIO( senseIOPtr, SIMg);
NewIOStartMachine( SIMg); /* Kick it off */
}
}
else {
SIMg->sensePBBusy = false;
}
UnblockInterrupts( oldSR); //<- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <-
// call the original PB's completion routine <SM10>
CompleteIO( originalIOPtr->savedResult, originalIOPtr, SIMg);
VMEnableUserCode();
}
/*********************************************************************************
CompResetDevice - reset device PB completion routine
*********************************************************************************/
pascal void
CompResetDevice( SIM_IO * ioPtr ) // <SM3>
{
int TargetID;
SIMglobals * SIMg;
SCSI_PB * oldRDPtr;
SCSI_PB * newRDPtr;
SIMg = (SIMglobals *)ioPtr->SIMg;
// oldRDPtr = ioPtr->pendingMsgPB; // this is what we want to move to
oldRDPtr = (SCSI_PB *)(SIMg->resetPBQ.qHead); // <LW3> removed call to comp routine
if ( !oldRDPtr) {
IfDebugStr("\pNo qHead in CompResetDevice");
SysError( dsIOCoreErr);
}
else if ( oldRDPtr != ioPtr->pendingMsgPB) {
IfDebugStr("\pqHead != ioPtr->pendingMsgPB (in CompResetDevice)");
SysError( dsIOCoreErr);
}
else
Dequeue( (QElemPtr)oldRDPtr, &SIMg->resetPBQ);
/* Get the next Device which needs to be reset */
newRDPtr = (SCSI_PB *)(SIMg->resetPBQ.qHead);
if ( newRDPtr )
{
TargetID = newRDPtr->scsiDevice.targetID;
SetupResetPB( TargetID, SIMg);
EnQIO( &(SIMg->resetDevPB), SIMg);
SIMg->resetDevPB.pendingMsgPB = (Ptr) newRDPtr; /* Remember who started this <LW2> */
NewIOStartMachine( SIMg); /* Kick it off */
}
}