/* File: XPT.c Contains: routines that implement the CAM Transport layer Entry points: OSErr SCSITrap (short scsiSelector, SCSI_PB *pbPtr ) Single entry point (Jerry's _NewSCSI trap). Uses scsiSelector to choose a routine from those shown below. Client Calls void SCSIAction (SCSI_PB *, Boolean syncCall, void (*callback)()) SCSI_PB *NewSCSI_PB (void) void DisposeSCSI_PB (SCSI_PB *) OS Module Calls OSErr InitSCSIMgr () long SCSIRegisterBus (SIMinitInfo * SIMinfo) Called to register a SCSI bus for use with the XPT. Several characteristics of the Bus are specified as well as the software entry points and the number of bytes required for a static data space (for global variables). The XPT returns a BusID that will be used for that Bus as well as a pointer to the allocated static space. SIMinitInfo is defined as shown: typedef struct { // directions are for SCSIRegisterBus call ( -> parm, <- result) long staticSize; // -> num bytes SIM needs for static vars void (*SIMinit)(); // -> pointer to the SIM init routine void (*SIMaction)(); // -> pointer to the SIM action routine Boolean oldCallCapable; // -> T if this SIM can handle old-API calls ushort busID; // <- bus number for the registered bus char * SIMstaticPtr; // <- alloc. ptr to the SIM's static vars } SIMinitInfo; OSErr SCSIDeregisterBus (ushort busID) void SCSIAsyncEvent (long what, ushort busID, ushort targetID, ushort lun, long dataBfr, long dataCnt) Written by: Paul Wolf Copyright: © 1992-1994 by Apple Computer, Inc., all rights reserved. Change History (most recent first): 2/1/94 DCB Making sure our busy patch version gets set. 1/31/94 DCB Fixed a bug in the way I checked the version number for the SCSI Busy patch. 1/25/94 DCB Getting rid of compiler warning. 1/25/94 DCB Don't block interrupts when polling since this screws up interrupt latency and besides we have the file system patches now. 1/9/94 pdw Added check for SCSIBusyPatch version number so that init can run and only patch when necessary. 1/5/94 pdw Converted SCSIGlobal uses to new format and rolled in new SCSIBusy Patch. 12/15/93 pdw Checking in for the build. 12/19/93 DCB Poll at all interrupt levels now since we might have a deferred task pending but not an interrupt. Also block interrupts at level 2 if not already blocked so we don't get re-entered when we don't expect it. This won't crash the SCSI Manager but could lead to calling a completion routine when syncUnsafe is non-zero which causes file system headaches until SCSI Busy gets fixed. 11/22/93 pdw Rolling in from . 11/11/93 pdw Added support back in for forceSyncAlways debugging. 11/10/93 pdw Added forceSync functionality which is used only when inDebugger and interrupts are blocked. This fixes the hang using Macsbug log which was caused by the FSCallAsync-always change to the File System. 11/5/93 pdw Series of attempts and re-attempts to fix various VM/FileShare problems. 10/29/93 DCB roll-in. 10/28/93 pdw Added fix: we didn't use to return the result in D0 in synchronous requests like we were supposed to. Now we do. 10/14/93 pdw roll-in. 10/12/93 pdw Added support for Synchronous data transfers, rewrote State Machine, message handling etc. 9/26/93 pdw Had to add a header file because of changes elsewhere. 9/12/93 pdw Removed SIMRegisterHAL support. Check for nonzero staticSize before allocating globals for SIM. Fixed up the reregister stuff a bit - moved common register/reregister stuff into sepearate routine. Added ability to alloc new SIM globals in Reregister. 9/9/93 pdw Lots of little changes. Name changes, temporary cache_bug stuff. 8/13/93 pdw Added check for nonZeroStatus to see if CallComp should do a virtual ID mapping. 7/17/93 pdw Lots of little things. 7/8/93 pdw Adding record events around Inquiry, GetVirtualID and Create Xref calls. 7/1/93 pdw Fixed RemoveRefNum bug where it would always return an error. We now "return" after setting up the noErr return value. 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. 5/29/93 PW Adding SCSIDebug.h to includes. 5/25/93 DCB Rollin from Ludwig. (The next item below) 5/20/93 DCB Added call to ciDebuggerPatch. This is a patch to _DebugUtil which prevents deferred completion routines from occuring when we are in a debugger. 5/5/93 PW Converted names to meanies-friendly names. Updated with latest from Ludwig stuff. 5/1/93 PW Added check for non-zero QLink to allow clients to see if we support pre-linked requests. Changed one last name: SCSI_RegisterWithNewXPT. 4/30/93 DCB Remove InitXPTRecorder if recording is turned off. 4/14/93 DCB Added ReRegisterAll Sims and fixed several bugs in the re-register SIM call. This allows us to install a new XPT at a later date. 4/13/93 MB (Really DCB) Fixing sign extension problem in GetRefNum. 3/26/93 PW Rolling in SuperMario changes. Removing useless RejectMsg stuff. Changed refNumXref stuff to use -1 in bus field instead of -1 in whole device ident as magic value. 3/20/93 PW Removing EnableUserCode/DisableUserCode calls from CallCompRoutine, SIMActionΙ - leaving it up to the SIM to do these calls instead (if necessary for that SIM). 3/1/93 DCB Changed parameters to CallCompRoutine to make life easier for developers. 2/17/93 PW Adding forceSyncAlways stuff (from PDM) because it will be useful for running benchmarks. 2/4/93 PW Enabled BusInquiry on PDM. Fixed forceSync stuff by making callback from XPTAction. 1/31/93 PW Update from the latest of Ludwig. Also changes required for PDM (will update Ludwig with these as needed myself). 1/30/93 PW Added glue code for calling completion routines (saving registers that were getting trashed by File Manager). 1/27/93 PW Added clear of diReserved in GetRefNum. 1/27/93 PW Removed "fix" for 'double completion on autosense" bug. Fixed it a much better way in CompAutoSense (a way that works). 1/12/93 DCB Added enable and disable user code calls around calls to completion routines to allow page faults in the completion routines. 1/8/93 PW Adding Disable/Enable user code calls around call to completion routine to fix async comp -> old-API deadlock bug. Added ISRs 8 through 0x0F to complete the set. Removed explicit Quadra int support from DispatchISR. 12/9/92 PW Got rid of selective DoVirtualMap stuff. Now we always do the mapping. 12/5/92 PW Handle returned fail from CallSIMaction. Added SetAsyncCallback partial support. Changed RefNum function names. Added InterleaveFactor support. Added check for invalid bus num in CallSIMaction. 11/20/92 PW Added some Includes that were removed from headers. Changed InitXPT & XPTregBus to reflect change from linked list of BusInfos to array. Removed DItoBus macro. Added maxbuses (16) restriction to XPTregBus. Added error checking. Added init of new fn ptrs to SIMinfo. Made Abort,Term,Reset async callbacks work. Changed sync from scSync flag to scComp=0. Removed EngineInq, Setup & DecodeDI. Removed calling of CompFn on XPT errs. Filled out XPT_ISR2 & 3. 11/1/92 DCB Fixed finalStatus length bug in CallCompRoutine that was stripping off the top byte of the scsiResult. 10/30/92 DCB Changed over to multiple ISR routines for improved efficiency 10/8/92 PW Added variable-sized SCSI_IO support. Lots of trivial name changes. 9/17/92 PW Added check for function code not supported. 9/14/92 DCB Added some Parameter Block validation. 8/1/92 PW Added comments. */ #include #include #include #include #include #include #include #include #include "CUtils.h" #include "ACAM.h" #include "SCSIDebug.h" #include "SCSIGlue.h" #include "BootItt.h" // for IsItt #include "XPT.h" #include "XPTpriv.h" #include "Recorder.h" #ifdef TESTGLOBALS extern short testGlobal3; extern short testGlobal2; extern short testGlobal1; #endif #define GetVBLQHdr() ((QHdrPtr) 0x0160) #define inVBL 6 // ************ Internal-only Function Prototypes ************** void SetRefNum( SCSI_Driver_PB *, XPTglobals * ); void GetRefNum( SCSI_Driver_PB *, XPTglobals * ); void RemoveRefNum( SCSI_Driver_PB *, XPTglobals * ); OSErr ReRegisterAllSIMs( XPTglobals *); OSErr XPTRegisterBus( SIMinitInfo * SIMinfo, XPTglobals *XPTg ); OSErr XPTDeregisterBus( ushort busID, XPTglobals *XPTg ); OSErr XPTReregisterBus( SIMinitInfo * SIMinfoPtr, XPTglobals *XPTg); void XPTRegisterCommon( SIMinitInfo * SIMinfoPtr); OSErr XPTKillXPT( XPTglobals *XPTg); void SyncWait( SCSI_PB *pbPtr, XPTglobals *XPTg ); void TryToRecover ( XPTglobals *XPTg); OSErr XPTAction( SCSI_PB *pbPtr, XPTglobals *XPTg ); Boolean OldSCSIExists( void); void InitXPTRecorder( long sizeOfTape ); void XPT_ISR0(void); void XPT_ISR1(void); void XPT_ISR2(void); void XPT_ISR3(void); void XPT_ISR4(void); void XPT_ISR5(void); void XPT_ISR6(void); void XPT_ISR7(void); void XPT_ISR8(void); void XPT_ISR9(void); void XPT_ISRa(void); void XPT_ISRb(void); void XPT_ISRc(void); void XPT_ISRd(void); void XPT_ISRe(void); void XPT_ISRf(void); /********************************************************************************* InitXPT - Initialize XPT Globals and stuff *********************************************************************************/ OSErr InitXPT( void ) { XPTglobals *XPTg; #ifdef TESTGLOBALS short testG; testG = testGlobal3; testG = testGlobal1; #endif InitRecorder(); // This gets a bit complicated if there is already a XPT installed. If there is // then we need to manage the transition from the old one to the new one. This // is accomplished by the routine ReRegisterAllSIMs: // // Allocate room for XPT globals and store XPTg ptr in magic spot in SCSIGlobals XPTg = (XPTglobals *) NewPtrSysClear( sizeof(XPTglobals)); //&&&& NewPtrSysClear if (XPTg == nil) return (memFullErr); // Build Driver List (for RefNum to DeviceIdent cross-ref) XPTg->Drivers = (DrvrList *) NewPtrSysClear( sizeof(DrvrList)); if ( XPTg->Drivers == nil ) { DisposPtr( (Ptr)XPTg); return (memFullErr); } XPTg->Drivers->DrvrCnt = 0; // Initialize static fields in the completionDT XPTg->completionDT.qType = dtQType; XPTg->completionDT.dtAddr = DeferredCall; if ( ISITT() ) { // Let the old instantiation of KillXPT know what version of busyPatch we currently use. // It will compare this against its own linked in value and remove its patch if they // aren't the same. SCSIGlobals->newBusyPatchVers = kbusyPatchVers; if( ReRegisterAllSIMs(XPTg) ) return(memFullErr); } else if ( OldSCSIExists() ) { XlateOldSCSIGlobals( XPTg ); // if New is replacing Old SCSI Mgr, xlate old's globals } Init_SCSIXlate(); InitXPTAsm(); if( (XPTg->debugPatch = ciDebuggerPatch( (Ptr) NGetTrapAddress( _DebugUtil, OSTrap ))) != nil ) NSetTrapAddress( (long) XPTg->debugPatch, _DebugUtil, OSTrap ); if (SCSIGlobals->busyPatchVers != kbusyPatchVers) { if( (SCSIGlobals->busyPatch = ciBusyPatch( (Ptr) NGetTrapAddress( _SCSIDispatch, ToolTrap ))) != nil ) { NSetTrapAddress( (long) SCSIGlobals->busyPatch, _SCSIDispatch, ToolTrap ); SCSIGlobals->busyPatchVers = kbusyPatchVers; } } SetXPTg( XPTg); InitSyncWait( XPTg ); // Setup the A089 Trap NSetTrapAddress ( (long)SCSIAtomic, 0x89, OSTrap ); return (0); } /*ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ OldSCSIExists - ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ*/ Boolean OldSCSIExists( void) { return( NGetTrapAddress(0xA815, ToolTrap) != NGetTrapAddress(UNIMPTRAP, ToolTrap) ); } /********************************************************************************* GetXPTg, SetXPTg - store and retrieve the XPT globals pointer *********************************************************************************/ #if 0 XPTglobals * GetXPTg( void ) { return(*(XPTglobals **)(*((Ptr *)0xC0C) + 0x1B4) ); } #endif void SetXPTg( XPTglobals *XPTg ) { *(XPTglobals **)(*((Ptr *)0xC0C) + 0x1B4) = XPTg; SCSIGlobals->tempXPTGInUse = 0; // Remember that we are using the real globals } void SetTempXPTg( XPTglobals *XPTg ) { SCSIGlobals->tempXPTGGlobals = XPTg; // Save the globals in a temporary spot SCSIGlobals->tempXPTGInUse = tempXPT; // Remember that we did this } /********************************************************************************* SCSITrap - This is our NewSCSI trap entry point *********************************************************************************/ OSErr SCSITrap( short scsiSelector, void * pbPtr ) { XPTglobals *XPTg; ushort busID; XPTg = GetXPTg(); if (scsiSelector == xptSCSIAction) { // Do a SCSI_PB action return (XPTAction( (SCSI_PB *)pbPtr, XPTg)); } switch (scsiSelector) { case xptSCSIRegisterBus: return (XPTRegisterBus( (SIMinitInfo *)pbPtr, XPTg)); break; case xptSCSIDeregisterBus: busID = ((SCSI_PB *)pbPtr)->scsiDevice.bus; return ( XPTDeregisterBus( busID, XPTg) ); break; case xptSCSIReregisterBus: return (XPTReregisterBus( (SIMinitInfo *)pbPtr, XPTg)); break; case xptSCSIKillXPT: return ( XPTKillXPT( XPTg) ); break; default: return (scsiFunctionNotAvailable); break; } } /********************************************************************************* XPTRegisterBus - Register new SIM/HBA with Xpt *********************************************************************************/ OSErr XPTRegisterBus (SIMinitInfo * SIMinfoPtr, XPTglobals *XPTg) { ushort busID; uchar * busG; BusInfo *thisBusInfoPtr; // Get next bus number and see if this one would be one too many if (XPTg->numBuses == kMaxBuses) { return( scsiTooManyBuses); } if (XPTg->numBuses > kMaxBuses) { IfDebugStr("\pWay too many bus registrations"); return( scsiTooManyBuses); } // Allocate memory for SIM's static vars if (SIMinfoPtr->staticSize) { // if SIM wants some statics busG = (uchar *) NewPtrSysClear((Size)SIMinfoPtr->staticSize); if (busG == 0) { IfDebugStr("\pCouldn't allocate bus statics"); return( memFullErr); } } // Get a BusInfo structure for this Bus's info // there are some BusInfos that are allocated in the XPT's globals already so // we try there first, if no room then we NewPtrSysClear another BusInfo spot. if (XPTg->numBuses < kNumBusInfos) { thisBusInfoPtr = &(XPTg->TopBusInfos[ XPTg->numBuses]); } else { thisBusInfoPtr = (BusInfo *)NewPtrSysClear(sizeof(BusInfo)); if (thisBusInfoPtr == 0) { return( memFullErr); } } // At this point, we've got everything a registration needs so we can consider // it a done deal. We will now increment the number of buses. // and find an empty bus number for this SIM. XPTg->numBuses+=1; for( busID = 0; busID < XPTg->numBuses; busID+=1 ) { if( XPTg->BusInfoPtr[busID] == nil ) break; } // We must put this pointer in XPT's BusInfoPtrs XPTg->BusInfoPtr[busID] = thisBusInfoPtr; // Set up the return values in the SIMinitInfo SIMinfoPtr->busID = busID; // -> bus number for the registered bus SIMinfoPtr->SIMstaticPtr = busG; // -> alloc. ptr to the SIM's static vars // Do all the common stuff - sticking our values in the SIM's init pb XPTRegisterCommon( SIMinfoPtr); // Set up the XPT's BusInfo with what's known about the HBA so far thisBusInfoPtr->initInfo = *SIMinfoPtr; // If we need to, allocate a new SCSI_IO for old API emulation. if ((SIMinfoPtr->ioPBSize > XPTg->xptIOpbSize) && SIMinfoPtr->oldCallCapable) { if (XPTg->xptIOpbSize) // if not first time thru, dispose old one DisposPtr( (char *)(XPTg->xptIOptr)); if (NewXPTioPB( SIMinfoPtr->ioPBSize, XPTg)) return (memFullErr); } // If we need to, adjust the maxIOpbSize field if (SIMinfoPtr->ioPBSize > XPTg->maxIOpbSize) XPTg->maxIOpbSize = SIMinfoPtr->ioPBSize; // If we got this far, call the SIMinit routine and return it's result as our result return ( (SIMinfoPtr->SIMinit) (SIMinfoPtr) ); } /********************************************************************************* XPTReregisterBus - Re-Register existing SIM/HBA with XPT *********************************************************************************/ OSErr XPTReregisterBus (SIMinitInfo * SIMinfoPtr, XPTglobals *XPTg) { ushort busID; BusInfo *thisBusInfoPtr; // If we are in the transition from one XPT to another then use the // temporary XPT globals if( SCSIGlobals->tempXPTGInUse == tempXPT ) XPTg = SCSIGlobals->tempXPTGGlobals; // Allocate memory for SIM's static vars if (SIMinfoPtr->SIMstaticPtr == 0) { // if the SIM wants new globals if (SIMinfoPtr->staticSize) { // and if SIM wants more than 0 SIMinfoPtr->SIMstaticPtr = (uchar *) NewPtrSysClear((Size)SIMinfoPtr->staticSize); if (SIMinfoPtr->SIMstaticPtr == 0) { IfDebugStr("\pCouldn't allocate bus statics"); return( memFullErr); } } } // Get the bus ID busID = SIMinfoPtr->busID; // Do all the common stuff - sticking our values in the SIM's init pb XPTRegisterCommon( SIMinfoPtr); // If we don't have a BusInfo structure for this Bus's info then go get one // there are some BusInfos that are allocated in the XPT's globals already so // we try there first, if no room then we NewPtrSysClear another BusInfo spot. if( XPTg->BusInfoPtr[busID] == nil ) { if (XPTg->numBuses < kNumBusInfos) { thisBusInfoPtr = &(XPTg->TopBusInfos[XPTg->numBuses]); } else { thisBusInfoPtr = (BusInfo *)NewPtrSysClear(sizeof(BusInfo)); if (thisBusInfoPtr == 0) { return( memFullErr); } } XPTg->numBuses+=1; XPTg->BusInfoPtr[busID] = thisBusInfoPtr; } // Copy SIMinitInfo data into XPT's BusInfoPtr XPTg->BusInfoPtr[busID]->initInfo = *SIMinfoPtr; // If we need to, allocate a new SCSI_IO for old API emulation. if ((SIMinfoPtr->ioPBSize > XPTg->xptIOpbSize) && SIMinfoPtr->oldCallCapable) { if (XPTg->xptIOptr) // if not first time thru, dispose old one DisposPtr( (char *)(XPTg->xptIOptr)); if (NewXPTioPB( SIMinfoPtr->ioPBSize, XPTg)) return (memFullErr); } // If we need to, adjust the maxIOpbSize field if (SIMinfoPtr->ioPBSize > XPTg->maxIOpbSize) XPTg->maxIOpbSize = SIMinfoPtr->ioPBSize; // We do not call the SIM's Initialization routine on reregister. Instead, // the SIM will get the data out upon return return ( noErr); } /********************************************************************************* XPTRegisterCommon - Common between Register and Reregister code *********************************************************************************/ void XPTRegisterCommon( SIMinitInfo * SIMinfoPtr) { // Set up the return values in the SIMinitInfo SIMinfoPtr->MakeCallback = XPTCallCompRoutine; SIMinfoPtr->EnteringSIM = VMDisableUserCode; SIMinfoPtr->ExitingSIM = VMEnableUserCode; switch ( SIMinfoPtr->busID) { case 0: SIMinfoPtr->XPT_ISR = XPT_ISR0; // -> ptr to the XPT's ISR (so SIM can install it) break; case 1: SIMinfoPtr->XPT_ISR = XPT_ISR1; break; case 2: SIMinfoPtr->XPT_ISR = XPT_ISR2; break; case 3: SIMinfoPtr->XPT_ISR = XPT_ISR3; break; case 4: SIMinfoPtr->XPT_ISR = XPT_ISR4; break; case 5: SIMinfoPtr->XPT_ISR = XPT_ISR5; break; case 6: SIMinfoPtr->XPT_ISR = XPT_ISR6; break; case 7: SIMinfoPtr->XPT_ISR = XPT_ISR7; break; case 8: SIMinfoPtr->XPT_ISR = XPT_ISR8; break; case 9: SIMinfoPtr->XPT_ISR = XPT_ISR9; break; case 0x0a: SIMinfoPtr->XPT_ISR = XPT_ISRa; break; case 0x0b: SIMinfoPtr->XPT_ISR = XPT_ISRb; break; case 0x0c: SIMinfoPtr->XPT_ISR = XPT_ISRc; break; case 0x0d: SIMinfoPtr->XPT_ISR = XPT_ISRd; break; case 0x0e: SIMinfoPtr->XPT_ISR = XPT_ISRe; break; case 0x0f: SIMinfoPtr->XPT_ISR = XPT_ISRf; break; } } /********************************************************************************* XPTDeregisterBus - Remove registration of SIM/HBA *********************************************************************************/ OSErr XPTDeregisterBus (ushort busID, XPTglobals *XPTg) { #pragma unused (busID) #pragma unused (XPTg) return (scsiFunctionNotAvailable); } /********************************************************************************* XPTKillXPT - Render XPT and its stuff inoperative (might be used for patching) *********************************************************************************/ OSErr XPTKillXPT ( XPTglobals *XPTg) { ulong * oldPatch; RemoveSyncWait(XPTg); // Get rid of our patch to syncWait // Disable patch by changing the enabled variable in the patch // this variable is embedded in the code 4 bytes before the // actual beginning of the code. oldPatch = (ulong *)(XPTg->debugPatch); *(--oldPatch) = 0; // disable the patch // Ditto for the SCSIBusy Patch if it changed. if( SCSIGlobals->newBusyPatchVers != kbusyPatchVers ) { // Let the new XPT know that there isn't a SCSI Busy patch. SCSIGlobals->busyPatchVers = 0; oldPatch = (ulong *)(SCSIGlobals->busyPatch); *(--oldPatch) = 0; // disable the patch } DisposPtr( (char *)(XPTg->xptIOptr)); DisposPtr((Ptr)XPTg); // Get rid of our globals // Anything else I missed? !!! return (noErr); } #if 0 /********************************************************************************* XPTAsyncEvent - notifies clients of event that they had registered for *********************************************************************************/ OSErr XPTAsyncEvent (SCSI_PB *pbPtr, XPTglobals *XPTg) { /*SCSIAsyncEvent (long what, ushort busID, ushort targetID, ushort lun, long dataBfr, long dataCnt)*/ return (scsiFunctionNotAvailable); } #endif /********************************************************************************* XPTAction - perform an Action (either XPT or pass it to SIM) *********************************************************************************/ OSErr XPTAction (SCSI_PB *pbPtr, XPTglobals *XPTg) { Boolean needSyncWait = false; // assume that the call won't need syncWait OSErr result = noErr; // error returned synchronously (result in D0) BusInfo *busInfoPtr; Boolean forceSync = false; pascal void (*oldCompletion)(SCSI_IO *); if (pbPtr->qLink) { // pdw F§ TOP pbPtr->scsiResult = scsiQLinkInvalid; return ( pbPtr->scsiResult); } // pdw F§ BOT // We have to see if the Function code is handled by the XPT or by the SIM switch (pbPtr->scsiFunctionCode) { case SCSIExecIO: // SIM case SCSIResetBus: // SIM // pdw case SCSIResetDevice: // SIM // pdw case SCSIReleaseQ: // SIM // pdw case SCSIAbortCommand: // SIM // pdw case SCSITerminateIO: // SIM // pdw #if forceSyncAlways // will poll always (not just when int level high) { #else if (SCSIGlobals->inDebugger && InterruptLevel() >1 ) { #endif forceSync = true; oldCompletion = pbPtr->scsiCompletion; pbPtr->scsiCompletion = 0; } ((SCSI_IO *)pbPtr)->savedA5 = getCurrentA5(); if( pbPtr->scsiCompletion == 0) // pdw needSyncWait = true; if( needSyncWait && XPTg->syncUnsafeCount ) { pbPtr->scsiResult = scsiBusy; return ( pbPtr->scsiResult); } if ( (((SCSI_PB *)pbPtr)->scsiDevice).bus >= XPTg->numBuses) { pbPtr->scsiResult = scsiBusInvalid; return ( pbPtr->scsiResult); } busInfoPtr = XPTg->BusInfoPtr[ (((SCSI_PB *)pbPtr)->scsiDevice).bus]; if( busInfoPtr ) (busInfoPtr->initInfo.SIMaction) (pbPtr, (busInfoPtr->initInfo).SIMstaticPtr); else{ pbPtr->scsiResult = scsiBusInvalid; return ( pbPtr->scsiResult); } if ( needSyncWait) { SyncWait( pbPtr, XPTg); if (forceSync) { if (oldCompletion!=0) { pbPtr->scsiCompletion = oldCompletion; XPTCallCompRoutine( (SCSI_IO *)pbPtr ); return (noErr); } } return (pbPtr->scsiResult); // return the error! (unlike Cyclone) } break; case SCSIBusInquiry: // SIM IfRecordEvent( *(long *)&pbPtr->scsiDevice, (long)'Inq-'); pbPtr->scsiResult = noErr; if ( pbPtr->scsiPBLength < sizeof(SCSIBusInquiryPB)) { pbPtr->scsiResult = scsiPBLengthError; } else if ( pbPtr->scsiDevice.bus == 0xff) { ((SCSIBusInquiryPB *)pbPtr)->scsiIOpbSize = sizeof(SCSI_IO); ((SCSIBusInquiryPB *)pbPtr)->scsiMaxIOpbSize = XPTg->maxIOpbSize; ((SCSIBusInquiryPB *)pbPtr)->scsiHiBusID = XPTg->numBuses - 1; BlockMove("Apple Computer \0",((SCSIBusInquiryPB *)pbPtr)->scsiSIMVendor,16); } else if ( pbPtr->scsiDevice.bus >= XPTg->numBuses) { pbPtr->scsiResult = scsiBusInvalid; } else { ((SCSIBusInquiryPB *)pbPtr)->scsiIOpbSize = sizeof(SCSI_IO); ((SCSIBusInquiryPB *)pbPtr)->scsiMaxIOpbSize = XPTg->maxIOpbSize; ((SCSIBusInquiryPB *)pbPtr)->scsiHiBusID = XPTg->numBuses - 1; pbPtr->scsiResult = CallSIMaction( pbPtr, XPTg); } IfRecordEvent( (long)(pbPtr->scsiResult), (long)'-Inq'); return ( pbPtr->scsiResult); break; case SCSISetAsyncCallback: // XPT & SIM result = CallSIMaction( pbPtr, XPTg); break; case SCSIGetVirtualIDInfo: // XPT IfRecordEvent( *(long *)(&(((SCSIGetVirtualIDInfoPB *)pbPtr)->scsiOldCallID)), (long)'Vid-'); GetVirtualIDInfo( (SCSIGetVirtualIDInfoPB *) pbPtr, XPTg); IfRecordEvent( *(long *)(&(((SCSIGetVirtualIDInfoPB *)pbPtr)->scsiOldCallID)), *(long *)&(pbPtr->scsiDevice)); break; case SCSICreateRefNumXref: // XPT IfRecordEvent( *(long *)(&(((SCSI_Driver_PB *)pbPtr)->scsiDevice)), (long)'Xrf-'); SetRefNum( (SCSI_Driver_PB *) pbPtr, XPTg); IfRecordEvent( *(long *)(&(((SCSI_Driver_PB *)pbPtr)->scsiDriver)), (long)(pbPtr->scsiResult)); break; case SCSILookupRefNumXref: // XPT GetRefNum( (SCSI_Driver_PB *) pbPtr, XPTg); break; case SCSIRemoveRefNumXref: // XPT RemoveRefNum( (SCSI_Driver_PB *) pbPtr, XPTg); break; case SCSIGenerateInterleaveID: // XPT ((SCSIGenerateInterleaveIDPB *) pbPtr)->scsiInterleaveID = XPTg->nextInterleave++; break; case SCSINop: // XPT break; default: result = CallSIMaction( pbPtr, XPTg); break; } return (result); } /********************************************************************************* CallSIMaction - perform an Action (either Xpt or pass it to SIM) *********************************************************************************/ OSErr CallSIMaction( void * pbPtr, XPTglobals * XPTg) { BusInfo *busInfoPtr; if ( (((SCSI_PB *)pbPtr)->scsiDevice).bus >= XPTg->numBuses) return (scsiBusInvalid); busInfoPtr = XPTg->BusInfoPtr[ (((SCSI_PB *)pbPtr)->scsiDevice).bus]; if( busInfoPtr ) { (busInfoPtr->initInfo.SIMaction) (pbPtr, (busInfoPtr->initInfo).SIMstaticPtr); return ( noErr); } return(scsiBusInvalid); } /********************************************************************************* SyncWait - wait for a synchronous request to be completed *********************************************************************************/ void SyncWait( SCSI_PB *pbPtr, XPTglobals *XPTg) { IfRecordEvent( (long)(pbPtr), (long)'Xsyw'); do { CheckInterrupts( XPTg); } while (pbPtr->scsiResult == scsiRequestInProgress); } /********************************************************************************* CheckInterrupts - *********************************************************************************/ void CheckInterrupts (XPTglobals * XPTg) { #if forceSyncAlways // will poll always (not just when int level high) DispatchISR(); #else // GROSS Hack Alert! // // In theory the private DT Queue is a SIM private since it is only used by the // pseudo DMA machines. However if I actually poll all SIMs at all interrupt // levels the overhead is such that basic operations that depend on syncwait start // to slow down. To get around this I only poll when I _have_ to which means if // there is something in our private deferred task queue or if it is busy (we are // working on soemthing and hadn't blocked ints at the VIA yet.) if (InterruptLevel() > 1 || SCSIGlobals->privDTQueue.qHead || SCSIGlobals->privDTQueue.qFlags ) { if (bset(inVBL, (char *)&((GetVBLQHdr()->qFlags)))) // make VBL busy: was it already? DispatchISR(); // yes: check interrupt sources else { DispatchISR(); // no: check interrupt sources bclr(inVBL, (char *)&((GetVBLQHdr()->qFlags))); // and clr busy bit (return to prev value) } } #endif if ( bclr( kbResetFromSync, &XPTg->flags )) TryToRecover( XPTg); } /********************************************************************************* TryToRecover - *********************************************************************************/ void TryToRecover ( XPTglobals *XPTg) { #pragma unused (XPTg) SCSIResetBusPB rstPB; Clear( &rstPB, sizeof(rstPB)); rstPB.scsiDevice.bus = 0; SCSIAction( (SCSI_PB *)&rstPB); rstPB.scsiDevice.bus = 1; SCSIAction( (SCSI_PB *)&rstPB); } /********************************************************************************* DispatchISR - *********************************************************************************/ void DispatchISR ( void) { short i; XPTglobals * XPTg; BusInfo * busInfoPtr; VMDisableUserCode(); // disable user code (so page faults won't happen) XPTg = GetXPTg(); for (i=0; i< XPTg->numBuses; i+=1) { busInfoPtr = XPTg->BusInfoPtr[i]; ((busInfoPtr->initInfo).SIMInterruptPoll) ( (busInfoPtr->initInfo).SIMstaticPtr); } VMEnableUserCode(); // enable user code (allow page faults) } /********************************************************************************* SetRefNum, GetNextRefNum, RemoveRefNum - *********************************************************************************/ void SetRefNum( SCSI_Driver_PB * pbPtr, XPTglobals * XPTg) { ulong index; Ptr temp; ulong size; pbPtr->scsiDevice.diReserved = 0; for ( index = 0; index < XPTg->Drivers->DrvrCnt; index+=1 ) { if ( pbPtr->scsiDevice == XPTg->Drivers->Drvr[index].DevID ) { pbPtr->scsiResult = scsiDeviceConflict; return; } } size = sizeof(DrvrList) + sizeof(RefInfo) * (index + 1); temp = NewPtrSysClear(size); if ( temp ) { BlockMove(XPTg->Drivers,temp,size - sizeof(RefInfo)); DisposPtr((Ptr)XPTg->Drivers); XPTg->Drivers = (DrvrList *)temp; } else { pbPtr->scsiResult = memFullErr; return; } XPTg->Drivers->DrvrCnt+=1; XPTg->Drivers->Drvr[index].DevID = pbPtr->scsiDevice; XPTg->Drivers->Drvr[index].DRefNum = pbPtr->scsiDriver; pbPtr->scsiResult = noErr; } void GetRefNum( SCSI_Driver_PB * pbPtr, XPTglobals * XPTg) { ulong index; pbPtr->scsiResult = noErr; /* Can't Fail */ if ( pbPtr->scsiDevice.bus == 0xFF ) { /* Marker for Give me the first in the list */ pbPtr->scsiDriver = 0; /* Not a driver */ if ( XPTg->Drivers->DrvrCnt ) pbPtr->scsiNextDevice = XPTg->Drivers->Drvr[0].DevID; /* First one in the list */ else pbPtr->scsiNextDevice.bus = 0xFF ; /* Nobody home */ return; } pbPtr->scsiDevice.diReserved = 0; /* Get rid of "unused" byte which might have (like the installer's setting of the high bit) extraneaous stuff in it! */ for ( index = 0; index < XPTg->Drivers->DrvrCnt; index+=1 ) { if ( pbPtr->scsiDevice == XPTg->Drivers->Drvr[index].DevID ) { pbPtr->scsiDriver = XPTg->Drivers->Drvr[index].DRefNum; if ( index + 1 == XPTg->Drivers->DrvrCnt ) pbPtr->scsiNextDevice.bus = 0xFF ; /* Nobody home */ else pbPtr->scsiNextDevice = XPTg->Drivers->Drvr[index + 1].DevID; return; } } pbPtr->scsiDriver = 0; /* Not a driver */ pbPtr->scsiNextDevice.bus = 0xFF ; /* Nobody home */ } void RemoveRefNum( SCSI_Driver_PB * pbPtr, XPTglobals * XPTg) { ulong index,remainder; pbPtr->scsiDevice.diReserved = 0; for ( index = 0; index < XPTg->Drivers->DrvrCnt; index+=1 ) { if ( pbPtr->scsiDevice == XPTg->Drivers->Drvr[index].DevID ) { for ( remainder = index; remainder < XPTg->Drivers->DrvrCnt - 1; remainder+=1 ) XPTg->Drivers->Drvr[remainder] = XPTg->Drivers->Drvr[remainder + 1]; XPTg->Drivers->DrvrCnt -= 1; pbPtr->scsiResult = noErr; /* Removed it */ return; //pdw } } pbPtr->scsiResult = scsiNoSuchXref; } /********************************************************************************* XPTCallCompRoutine - Called by the SIM's CompleteIO *********************************************************************************/ void XPTCallCompRoutine( SCSI_IO * ioPtr ) // { pascal void (*fn)(SCSI_IO *); // Ptr oldA5; XPTglobals * XPTg; //--- If we need to do virtual ID mapping, and result==noErr then do it // pdw XPTg = GetXPTg(); if (XPTg->oldIDBusNumber[ioPtr->scsiDevice.targetID]<0) { if ( ioPtr->scsiFunctionCode == SCSIExecIO) // pdw F§ if ( ioPtr->scsiResult==noErr || ioPtr->scsiResult==scsiNonZeroStatus || ioPtr->scsiResult==scsiDataRunError ) if( XPTg->BusInfoPtr[ioPtr->scsiDevice.bus]->initInfo.oldCallCapable) XPTg->oldIDBusNumber[ioPtr->scsiDevice.targetID] = ioPtr->scsiDevice.bus; } ioPtr->qLink = 0; // set qLink back to NIL in case pb is reused // pdw F§ //--- Call the completion routine (with proper A5) as long as routine ptr isn't NIL // scsiCompletion was moved into the header - therefore we can call it for any pb if( (fn=ioPtr->scsiCompletion) != nil ) { // oldA5 = getCurrentA5(); now it's done in CallCompRoutineGlue IfRecordEvent( (long)(ioPtr), (long)'xcC-'); CallCompRoutineGlue( ioPtr, &(XPTg->completionDT)); // MAKE CALLBACK IfRecordEvent( (long)(fn), (long)'-xcC'); } } /********************************************************************************* ReRegisterAllSIMs - *********************************************************************************/ OSErr ReRegisterAllSIMs( XPTglobals *XPTg ) { long OldXPT; Boolean *oldSIMs; ulong id; SCSIGetVirtualIDInfoPB dIdent; SCSI_Driver_PB drvrPB; SCSIBusInquiryPB scPB; uchar busID; uchar highBusID; SCSI_PB reRegister; // Setup our globals in a temporary spot so we don't step on any existing globals // Use GetVirtualID to build up a list of virtual IDs // Use BusInquiry to build up a list of existing SIMs // SetTrapAddress to the new XPT and remember the address of the old one. // Use the old XPT to send a ReRegisterSIM call to each SIM // Call KillXPT to get rid of the old XPT (Note Kill XPT can't call SetTrapAddress!!!) // Put our Globals in the normal spot // Install any patches (such as vSyncWait) that we will use (this MUST be done AFTER KillXPT) // If the normal internal SIMs didn't already exist then tell InitItt to install them SetTempXPTg( XPTg); // Use the old XPT to find all of the virtual IDs Clear((char *)&dIdent,sizeof(dIdent)); for( id = 0; id < 7; id+=1 ) { dIdent.scsiOldCallID = id; dIdent.scsiFunctionCode = SCSIGetVirtualIDInfo; dIdent.scsiCompletion = nil; dIdent.scsiPBLength = sizeof(SCSIGetVirtualIDInfoPB); SCSIAction((SCSI_PB *) &dIdent); if( dIdent.scsiExists ) XPTg->oldIDBusNumber[id] = dIdent.scsiDevice.bus; else XPTg->oldIDBusNumber[id] = -1; } // Now go get all of the existing Drivers which have been registered Clear((char *)&drvrPB,sizeof(drvrPB)); /* Get the devIdent of the first driver registered with Itt */ drvrPB.scsiDevice.bus = 0xff; /* Give us the first one in the list */ drvrPB.scsiPBLength = sizeof(drvrPB); drvrPB.scsiFunctionCode = SCSILookupRefNumXref; SCSIAction( (SCSI_PB *) &drvrPB ); drvrPB.scsiDevice = drvrPB.scsiNextDevice; /* Get The first one in the list */ while( drvrPB.scsiDevice.bus != 0xff ) { SCSIAction( (SCSI_PB *) &drvrPB ); SetRefNum(&drvrPB,XPTg); /* Register with the new set of globals */ drvrPB.scsiDevice = drvrPB.scsiNextDevice; /* Get The next one in the list */ } // Look for existing SIMs Clear((char *)&scPB,sizeof(scPB)); *(long *)&(scPB.scsiDevice) = 0xff; // XPT Inquiry scPB.scsiFunctionCode = SCSIBusInquiry; scPB.scsiPBLength = sizeof(SCSIBusInquiryPB); SCSIAction( (SCSI_PB *) &scPB ); // Get info for the XPT highBusID = scPB.scsiHiBusID; if( (oldSIMs = (Boolean *)NewPtrSysClear(sizeof(Boolean) * (highBusID + 1))) == 0 ) { IfDebugStr("\pNot enough memory to switch XPTs"); // Yikes, this would be really bad... return(memFullErr); } for( busID = 0; busID <= highBusID; busID+=1 ) { scPB.scsiDevice.bus = busID; scPB.scsiFunctionCode = SCSIBusInquiry; scPB.scsiPBLength = sizeof(SCSIBusInquiryPB); if( SCSIAction( (SCSI_PB *) &scPB ) == noErr) oldSIMs[busID] = true; } // Remember the old TrapAddress and setup the new one OldXPT = NGetTrapAddress(SCSIATOM, OSTrap); NSetTrapAddress ( (long)SCSIAtomic, 0x89, OSTrap ); // And... Re-Register all existing SIMs Clear((char *)&reRegister,sizeof(reRegister)); for( busID = 0; busID <= highBusID; busID+=1 ) { if( oldSIMs[busID] ) { reRegister.scsiPBLength = sizeof(reRegister); reRegister.scsiDevice.bus = busID; reRegister.scsiFunctionCode = SCSIRegisterWithNewXPT; CallOldXPT( &reRegister,xptSCSIAction,OldXPT ); } } // Get rid of the old XPT and get out of here CallOldXPT(0, xptSCSIKillXPT, OldXPT); SetXPTg(XPTg); // Put the real XPT Globals in place DisposPtr((Ptr)oldSIMs); } /********************************************************************************* XPT_ISR0 - *********************************************************************************/ #if 0 void XPT_ISR0 (void) { BusInfo * busInfoPtr; VMDisableUserCode(); // disable user code (so page faults won't happen) // vv busInfoPtr = GetXPTg()->BusInfoPtr[0]; ((busInfoPtr->initInfo).SIM_ISR)( (busInfoPtr->initInfo).SIMstaticPtr); VMEnableUserCode(); // enable user code (allow page faults) } void XPT_ISR1 (void) { BusInfo * busInfoPtr; VMDisableUserCode(); // disable user code (so page faults won't happen) // vv busInfoPtr = GetXPTg()->BusInfoPtr[1]; ((busInfoPtr->initInfo).SIM_ISR)((busInfoPtr->initInfo).SIMstaticPtr); VMEnableUserCode(); // enable user code (allow page faults) } #endif void XPT_ISR2 (void) { BusInfo * busInfoPtr; VMDisableUserCode(); // disable user code (so page faults won't happen) // vv busInfoPtr = GetXPTg()->BusInfoPtr[2]; ((busInfoPtr->initInfo).SIM_ISR)((busInfoPtr->initInfo).SIMstaticPtr); VMEnableUserCode(); // enable user code (allow page faults) } void XPT_ISR3 (void) { BusInfo * busInfoPtr; VMDisableUserCode(); // disable user code (so page faults won't happen) // vv busInfoPtr = GetXPTg()->BusInfoPtr[3]; ((busInfoPtr->initInfo).SIM_ISR)((busInfoPtr->initInfo).SIMstaticPtr); VMEnableUserCode(); // enable user code (allow page faults) } void XPT_ISR4 (void) { BusInfo * busInfoPtr; VMDisableUserCode(); // disable user code (so page faults won't happen) // vv busInfoPtr = GetXPTg()->BusInfoPtr[4]; ((busInfoPtr->initInfo).SIM_ISR)((busInfoPtr->initInfo).SIMstaticPtr); VMEnableUserCode(); // enable user code (allow page faults) } void XPT_ISR5 (void) { BusInfo * busInfoPtr; VMDisableUserCode(); // disable user code (so page faults won't happen) // vv busInfoPtr = GetXPTg()->BusInfoPtr[5]; ((busInfoPtr->initInfo).SIM_ISR)((busInfoPtr->initInfo).SIMstaticPtr); VMEnableUserCode(); // enable user code (allow page faults) } void XPT_ISR6 (void) { BusInfo * busInfoPtr; VMDisableUserCode(); // disable user code (so page faults won't happen) // vv busInfoPtr = GetXPTg()->BusInfoPtr[6]; ((busInfoPtr->initInfo).SIM_ISR)((busInfoPtr->initInfo).SIMstaticPtr); VMEnableUserCode(); // enable user code (allow page faults) } void XPT_ISR7 (void) { BusInfo * busInfoPtr; VMDisableUserCode(); // disable user code (so page faults won't happen) // vv busInfoPtr = GetXPTg()->BusInfoPtr[7]; ((busInfoPtr->initInfo).SIM_ISR)((busInfoPtr->initInfo).SIMstaticPtr); VMEnableUserCode(); // enable user code (allow page faults) } void XPT_ISR8 (void) { BusInfo * busInfoPtr; VMDisableUserCode(); // disable user code (so page faults won't happen) // vv busInfoPtr = GetXPTg()->BusInfoPtr[8]; ((busInfoPtr->initInfo).SIM_ISR)((busInfoPtr->initInfo).SIMstaticPtr); VMEnableUserCode(); // enable user code (allow page faults) } void XPT_ISR9 (void) { BusInfo * busInfoPtr; VMDisableUserCode(); // disable user code (so page faults won't happen) // vv busInfoPtr = GetXPTg()->BusInfoPtr[9]; ((busInfoPtr->initInfo).SIM_ISR)((busInfoPtr->initInfo).SIMstaticPtr); VMEnableUserCode(); // enable user code (allow page faults) } void XPT_ISRa (void) { BusInfo * busInfoPtr; VMDisableUserCode(); // disable user code (so page faults won't happen) // vv busInfoPtr = GetXPTg()->BusInfoPtr[0x0a]; ((busInfoPtr->initInfo).SIM_ISR)((busInfoPtr->initInfo).SIMstaticPtr); VMEnableUserCode(); // enable user code (allow page faults) } void XPT_ISRb (void) { BusInfo * busInfoPtr; VMDisableUserCode(); // disable user code (so page faults won't happen) // vv busInfoPtr = GetXPTg()->BusInfoPtr[0x0b]; ((busInfoPtr->initInfo).SIM_ISR)((busInfoPtr->initInfo).SIMstaticPtr); VMEnableUserCode(); // enable user code (allow page faults) } void XPT_ISRc (void) { BusInfo * busInfoPtr; VMDisableUserCode(); // disable user code (so page faults won't happen) // vv busInfoPtr = GetXPTg()->BusInfoPtr[0x0c]; ((busInfoPtr->initInfo).SIM_ISR)((busInfoPtr->initInfo).SIMstaticPtr); VMEnableUserCode(); // enable user code (allow page faults) } void XPT_ISRd (void) { BusInfo * busInfoPtr; VMDisableUserCode(); // disable user code (so page faults won't happen) // vv busInfoPtr = GetXPTg()->BusInfoPtr[0x0d]; ((busInfoPtr->initInfo).SIM_ISR)((busInfoPtr->initInfo).SIMstaticPtr); VMEnableUserCode(); // enable user code (allow page faults) } void XPT_ISRe (void) { BusInfo * busInfoPtr; VMDisableUserCode(); // disable user code (so page faults won't happen) // vv busInfoPtr = GetXPTg()->BusInfoPtr[0x0e]; ((busInfoPtr->initInfo).SIM_ISR)((busInfoPtr->initInfo).SIMstaticPtr); VMEnableUserCode(); // enable user code (allow page faults) } void XPT_ISRf (void) { BusInfo * busInfoPtr; VMDisableUserCode(); // disable user code (so page faults won't happen) // vv busInfoPtr = GetXPTg()->BusInfoPtr[0x0f]; ((busInfoPtr->initInfo).SIM_ISR)((busInfoPtr->initInfo).SIMstaticPtr); VMEnableUserCode(); // enable user code (allow page faults) }