/* File: SIMQ.c Contains: routines that implement the Queueing mechainism of the SIM layer for the Apple SIM Core Entry points: Written by: Paul Wolf Copyright: © 1992-1993 by Apple Computer, Inc., all rights reserved. Change History (most recent first): 12/19/93 DCB Clear hdshkRemainder in the PB when we enqueue it. This is to help solve the handshake across scatter gather boundries problem. 11/22/93 pdw Rolling in from . 11/8/93 pdw Cleared firstError upon entry. Changed comments. 10/14/93 pdw roll-in. 10/12/93 pdw Added support for Synchronous data transfers, rewrote State Machine, message handling etc. 9/13/93 pdw Roll-in from SuperMario. 9/13/93 pdw Changed the risky slash pattern. Fixed ResetDevice bug by reverting to previous criteria in SwitchQ. It will now only switch those IOs that are not idle. 9/12/93 pdw Changed EnQIO to reflect name change of EnqueueHead to CEnqueueHead (to avoid conflict with new ROM routine). 7/17/93 pdw Lots of little things. Also changed ResetQueues to treat idle/non-idle pbs a little differently. 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 include of SCSIDebug.h 5/25/93 DCB Rollin from Ludwig. (The next item below) 5/21/93 PW Adding SIMg parameter to ValidatePB so that it can check hostID. 5/5/93 PW Converted names to meanies-friendly names. Updated with latest from Ludwig stuff. 3/8/93 PW Eliminated an ==0 to get rid of 3 instructions. 3/3/93 PW Added line to change defaultDisc when EnQing with an item already in Q. 3/1/93 DCB Added an ID parameter to the ResetQ function so that it could be used for ResetDevice as well as ResetBus. Also moved the timer setup into EnQIO. 2/18/93 DCB Changed Reset Queue's behavior to match the documentation. Only those IOs actually started are dequeued after a reset. 1/27/93 PW Removed silly DItoTarget/LUN stuff. 12/17/92 DCB Added a parameter block clear in EnQIO. 12/5/92 PW Added SCSI.h include. 11/20/92 DCB Support for async Abort, Terminate and Reset Device 10/30/92 DCB Added initial value set for SIMprivFlags 10/8/92 PW Lots of name changes. 9/11/92 DCB Added queue support for the (singular) ResetDevice Parameter block. 8/31/92 DCB Added FindIO PB function for Terminate and Abort PB. Also changed DeQIO to use the ioQueue field that I added to the SCSI_IO parameter block. Finally - added SwitchQ to move a single ioPB from one Q to another. */ #include #include #include #include #include #include #include "ACAM.h" #include "CUtils.h" #include "QueueUtils.h" #include "XPT.h" #include "XPTpriv.h" #include "SIMCore.h" #include "SIMCorePriv.h" #include "SIMQ.h" #include "SCSIDebug.h" #include "Recorder.h" /********* External Function Prototypes **********/ /********************************************************************************* EnQIO - *********************************************************************************/ void EnQIO( SIM_IO *ioPtr, SIMglobals * SIMg) { ioPtr->ioStat = kPBidle; ioPtr->scsiOldCallResult = scsiRequestInProgress; ioPtr->ioEvent = 0; /* No messages yet */ ioPtr->firstError = 0; ioPtr->savedResult = 0; ioPtr->SIMg = (Ptr)SIMg; /* Save this for the autosense/device reset completion routine */ ioPtr->pendingMsgPB = nil; /* */ ioPtr->SIMprivFlags = 0; /* */ ioPtr->scsiResultFlags = 0; /* */ ioPtr->ioQueue = (Ptr)&SIMg->qHdr; // Everything gets added to the IOq first ioPtr->hdshkRemainder = 0; /* */ /* Convert format to seconds and start timer */ if( !ioPtr->scsiTimeout ) ioPtr->scTimer = 0; else if( ioPtr->scsiTimeout < 0 ) /* µSeconds -> 1 second */ ioPtr->scTimer = 1; /* !!! This actually could be longer than a second! */ else ioPtr->scTimer = (ioPtr->scsiTimeout >> 10) + 1; /* Close enough to seconds...*/ ioPtr->scsiSCSIstatus = 0; // if (SIMg->qHdr.qHead) SIMg->defaultDisc = true; if ( ioPtr->scsiFlags & scsiSIMQHead) { IfRecordEvent( (long)ioPtr, (long)'EnQH'); CEnqueueHead((QElemPtr)ioPtr, &SIMg->qHdr); } else { IfRecordEvent( (long)ioPtr, (long)'EnQT'); Enqueue((QElemPtr)ioPtr, &SIMg->qHdr); } } /********************************************************************************* DeQIO - *********************************************************************************/ void DeQIO( SIM_IO *ioPtr, SIMglobals * SIMg) // { QHdrPtr IOQHdr; IOQHdr = (QHdrPtr)ioPtr->ioQueue; // IfRecordEvent( (long)ioPtr, (long)'DeQ '); if( !IOQHdr->qHead ) return; /* Sorry - no queue */ // In case of CompleteIO before getting sense (i.e. from ResetX, AbortCmd or TermIO) if( ioPtr->ioStat == kNeedsSense ) SIMg->senseWaiting -=1; if (Dequeue((QElemPtr)ioPtr, IOQHdr)) { IfDebugStr("\pDequeue in DeQIO failed"); SysError( dsIOCoreErr); } } /********************************************************************************* GetNextReadyIO - scan Q for next available IO *********************************************************************************/ SIM_IO * GetNextReadyIO( SIMglobals * SIMg) { SIM_IO * qPtr; if( SIMg->SIMBusy ) return(0); /* Even if we reject a PB the XPT might attempt to restart the machine */ // First look in the immediate queue qPtr = (SIM_IO *)SIMg->immedQHdr.qHead; while (qPtr != 0) { if (qPtr->pendingMsgPB != nil && !(qPtr->ioEvent & kmMsgSent)) break; qPtr = (SIM_IO *)(qPtr->qLink); } if( !qPtr || (qPtr->ioEvent & kmMsgSent) ) { // If we already sent a message... qPtr = (SIM_IO *)SIMg->qHdr.qHead; while (qPtr) { if (qPtr->ioStat == kPBidle) { if ( !FindUsedLUN( qPtr->scsiDevice.targetID, qPtr->scsiDevice.LUN)) break; } qPtr = (SIM_IO *)(qPtr->qLink); } } IfRecordEvent( (long)qPtr, (long)'GtIO'); return (qPtr); } /********************************************************************************* ResetQueues - Moves all non-idle IOs from ExecIO Q to the resetQ that match the target ID (an ID of 0xFF means all devices). This allows us to continue accepting new IOs while dispensing with all of those currently in the queue. The reason that we don't discriminate on a LUN basis is because both Reset Bus and Reset Device affect an entire device. *********************************************************************************/ void ResetQueues( SIMglobals * SIMg, uchar ID ) // { ushort oldSR; SIM_IO * qPtr; oldSR = BlockInterrupts(); //-> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> -> IfRecordEvent( (long)0, (long)'RsQu'); qPtr = (SIM_IO *)SIMg->qHdr.qHead; while( qPtr ) { if ( qPtr->ioStat !=kIdle && (ID==0xFF || qPtr->scsiDevice.targetID == ID ) ) { Dequeue((QElemPtr)qPtr,(QHdrPtr) &SIMg->qHdr); Enqueue((QElemPtr)qPtr,(QHdrPtr) &SIMg->resetHdr); qPtr->ioQueue = (Ptr)&SIMg->resetHdr; // remember that we moved this queue element } qPtr = (SIM_IO *) qPtr->qLink; } UnblockInterrupts( oldSR); //<- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- <- } /********************************************************************************* GetNextAutoSense - scan Q for next IO which is waiting for autosense *********************************************************************************/ SIM_IO * GetNextAutoSense( SIMglobals * SIMg) { SIM_IO * qPtr; qPtr = (SIM_IO *)SIMg->qHdr.qHead; while (qPtr) { if (qPtr->ioStat == kNeedsSense) break; qPtr = (SIM_IO *)(qPtr->qLink); } IfRecordEvent( (long)qPtr, (long)'GtAS'); return (qPtr); } /********************************************************************************* FindIO - scan Q for a particular IO *********************************************************************************/ SIM_IO * // FindIO( SIM_IO * ioPtr, SIMglobals * SIMg) { SIM_IO * qPtr; qPtr = (SIM_IO *)SIMg->qHdr.qHead; while (qPtr) { if ( qPtr == ioPtr ) break; qPtr = (SIM_IO *)(qPtr->qLink); } return (qPtr); } /********************************************************************************* SwitchQ - Move an ioPB from one queue to another *********************************************************************************/ void // SwitchQ( SIM_IO * ioPB, QHdrPtr newQ) { if ( Dequeue((QElemPtr)ioPB, (QHdrPtr)ioPB->ioQueue) ) { IfDebugStr("\pDequeue failed in SwitchQ"); } Enqueue( (QElemPtr)ioPB, newQ); ioPB->ioQueue = (Ptr) newQ; IfRecordEvent( (long)ioPB, (long)'SwQ '); return; } /********************************************************************************* FindIOFromID - scan Q for the first IO associated with a SCSI device *********************************************************************************/ SIM_IO * // FindIOFromID( uchar TargetID, SIMglobals * SIMg) { SIM_IO * qPtr; qPtr = (SIM_IO *)SIMg->qHdr.qHead; while (qPtr) { if( (qPtr->scsiDevice).targetID == TargetID ) break; qPtr = (SIM_IO *)(qPtr->qLink); } IfRecordEvent( (long)qPtr, (long)'ioID'); return (qPtr); }