Source to iokit/Drivers/scsi/drvCurio/curio.cpp


Enter a symbol's name here to quickly find it.

/*
 * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * http://www.apple.com/publicsource and read it before using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

	/* Copyright 1997-1999 Apple Computer Inc. All Rights Reserved.			*/
	/* @author  Martin Minow												*/
	/* Edit History															*/
	/* 1997.02.13	MM	Initial conversion from AMDPCSCSIDriver sources.	*/
	/*																		*/




#include <string.h>					/* bzero is defined in string.h */
#include <mach/clock_types.h>
#include <libkern/OSByteOrder.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOInterruptEventSource.h>
#include <IOKit/pci/IOPCIDevice.h>
#include <IOKit/ppc/IODBDMA.h>
#include <IOKit/system.h>			/* Definition of flush_dcache	*/
#include <IOKit/scsi/IOSCSIParallelInterface.h>

#include "curio.h"




 	extern void		IOGetTime( mach_timespec_t *clock_time );
	extern void		kprintf( const char *, ... );
    extern void     call_kdp();     // for debugging


#if CustomMiniMon
	extern globals		g;  /**** Use custom MiniMon's globals   ****/
	extern UInt32       gMESH_DBDMA, gMESH_DBDMA_Phys;
#else
//	       globals		g;	/**** Instantiate the globals ****/
	static globals		g;	/**** Instantiate the globals ****/
#endif /* CustomMiniMon */


	OSDefineMetaClassAndStructors( CurioSCSIController, IOSCSIParallelController )	;


#define kIOReturnParityError		kIOReturnIOError	/// ??? add to IOReturn.h
#define kIOReturnSelectionError		kIOReturnIOError	/// ??? add to IOReturn.h


    enum 						/*****	values for g.intLevel:		*****/
    {
        kLevelISR       = 0x80,		/* In Interrupt Service Routine		*/
        kLevelLocked    = 0x40,		/* Curio interrupts locked out		*/
        kLevelSIH       = 0x20,		/* In Secondary Interrupt Handler	*/
        kLevelLatched   = 0x10		/* Interrupt latched				*/
    };



#if USE_ELG && !CustomMiniMon
static void AllocateEventLog( UInt32 size )
{
    if ( g.evLogBuf )
		return;

	g.evLogFlag = 0;            /* assume insufficient memory   */
	g.evLogBuf = (UInt8*)kalloc( size );
	if ( !g.evLogBuf )
	{
		kprintf( "AllocateEventLog - CurioSCSIController evLog allocation failed " );
		return;
	}

	bzero( g.evLogBuf, size );
	g.evLogBufp	= g.evLogBuf;
	g.evLogBufe	= g.evLogBufp + kEvLogSize - 0x20; // ??? overran buffer?
	g.evLogFlag  = 0xFEEDBEEF;
//	g.evLogFlag  = 'step';
//  g.evLogFlag  = 0x0333;
    return;
}/* end AllocateEventLog */


static void EvLog( UInt32 a, UInt32 b, UInt32 ascii, char* str )
{
	register UInt32		*lp;           /* Long pointer      */
	mach_timespec_t		time;

	if ( g.evLogFlag == 0 )
		return;

	IOGetTime( &time );

    lp = (UInt32*)g.evLogBufp;
    g.evLogBufp += 0x10;

    if ( g.evLogBufp >= g.evLogBufe )       /* handle buffer wrap around if any */
    {    g.evLogBufp  = g.evLogBuf;
        if ( g.evLogFlag != 0xFEEDBEEF )
            g.evLogFlag = 0;                /* stop tracing if wrap undesired   */
    }

        /* compose interrupt level with 3 byte time stamp:  */

	*lp++ = (g.intLevel << 24) | ((time.tv_nsec >> 10) & 0x003FFFFF);   // ~ 1 microsec resolution
    *lp++ = a;
    *lp++ = b;
    *lp   = ascii;

    if( g.evLogFlag == 'step' )
	{	char	code[ 5 ];
		*(UInt32*)&code = ascii;		code[ 4 ] = 0;
	//	kprintf( "%8x curio: %8x %8x %s        %s\n", time.tv_nsec>>10, a, b, code, str );
		kprintf( "%8x curio: %8x %8x %s\n", time.tv_nsec>>10, a, b, code );
	}

    return;
}/* end EvLog */


static void Pause( UInt32 a, UInt32 b, UInt32 ascii, char* str )
{
    char        work [ 256 ];
    char        name[] = "CurioSCSIController:";
    char        *bp = work;
    UInt8       x;
    int         i;


    EvLog( a, b, ascii, str );
    EvLog( '****', '** P', 'ause', "*** Pause" );

    bcopy( name, bp, sizeof( name ) );
    bp += sizeof( name ) - 1;

    *bp++ = '{';                               // prepend p1 in hex:
    for ( i = 7; i >= 0; --i )
    {
        x = a & 0x0F;
        if ( x < 10 )
             x += '0';
        else x += 'A' - 10;
        bp[ i ] = x;
        a >>= 4;
    }
    bp += 8;

    *bp++ = ' ';                               // prepend p2 in hex:

    for ( i = 7; i >= 0; --i )
    {
        x = b & 0x0F;
        if ( x < 10 )
             x += '0';
        else x += 'A' - 10;
        bp[ i ] = x;
        b >>= 4;
    }
    bp += 8;
    *bp++ = '}';

    *bp++ = ' ';

    for ( i = sizeof( work ) - (int)(bp - work); i && (*bp++ = *str++); --i )   ;

//	kprintf( work );
	panic( work );
//  call_kdp();         // ??? use kdp=3 in boot parameters
    return;
}/* end Pause */
#endif /* not CustomMiniMon */



bool CurioSCSIController::configure(	IOService			*provider,
										SCSIControllerInfo	*controllerInfo )
{
	IOReturn		ioReturn = kIOReturnInternalError;


	g.intLevel		= 0;
	g.curioInstance	= this;
#if USE_ELG
    AllocateEventLog( kEvLogSize );
	ELG( g.evLogBufp, &g.evLogFlag, 'Curo', "configure - event logging set up." );
#endif /* USE_ELG */

	ELG( this, provider, 'Cnfg', "configure" );

    fProvider = (IOPCIDevice*)provider;

	ioReturn = initHardware();
	if ( ioReturn != kIOReturnSuccess )
		return false;

		/* Register our interrupt handler routine:	*/

    fInterruptEvent = IOInterruptEventSource::interruptEventSource(
									(OSObject*)this,

                    (IOInterruptEventAction)&CurioSCSIController::interruptOccurred,
									provider,
									 0 );

    if ( fInterruptEvent == NULL )
    {
		ELG( 0, 0, 'IES-', "CurioSCSIController::configure - can't register interrupt action" );
        return false;
    }

    getWorkLoop()->addEventSource( fInterruptEvent );
    fInterruptEvent->enable();

		/* allocate a big-endian memory cursor:	*/

    fMemoryCursor = IOBigMemoryCursor::withSpecification( kMaxDMATransfer, kMaxDMATransfer );
    if ( fMemoryCursor == NULL )
    {
		ELG( 0, kMaxDMATransfer, 'Mem-', "CurioSCSIController::start - IOBigMemoryCursor::withSpecification NG" );
        return false;
    }


		/* Fill in the  SCSIControllerInfo structure and return:	*/

        controllerInfo->initiatorId			= 7;

	controllerInfo->maxTargetsSupported		= 8;
	controllerInfo->maxLunsSupported		= 8;

	controllerInfo->minTransferPeriodpS		= 200000;	/* picoSecs for 5 MHz	*/
	controllerInfo->maxTransferOffset		= 0;
	controllerInfo->maxTransferWidth		= 1;

	controllerInfo->maxCommandsPerController= 0;		// 0 is unlimited
	controllerInfo->maxCommandsPerTarget	= 0;
	controllerInfo->maxCommandsPerLun		= 0;

	controllerInfo->tagAllocationMethod		= kTagAllocationPerLun;
	controllerInfo->maxTags					= 256;

	controllerInfo->commandPrivateDataSize	= sizeof( PrivCmdData );

	controllerInfo->disableCancelCommands	= false;

	return true;
}/* end configure */


void CurioSCSIController::executeCommand( IOSCSICommand *scsiCommand )
{
	SCSICDBInfo			scsiCDB;
	SCSITargetParms		targetParms;
	UInt8				msgByte;
	bool				rc;



	if ( fCmd || fBusBusy )
	{
		ELG( fCmd, scsiCommand, 'Busy', "CurioSCSIController::executeCommand - bus busy so bounce this cmd" );
		disableCommands();
		rescheduleCommand( scsiCommand );
		return;
	}

    fCmd 		= scsiCommand;
    fCmdData	= (PrivCmdData*)scsiCommand->getCommandData();

    scsiCommand->getTargetLun( &fCurrentTargetLun );
    scsiCommand->getCDB( &scsiCDB );
    scsiCommand->getDevice(kIOSCSIDevice)->getTargetParms( &targetParms );

	ELG(	scsiCommand,
			*(UInt16*)&fCurrentTargetLun<<16 | (scsiCDB.cdbTag & 0xFF),
			'Exec',			"CurioSCSIController::executeCommand" );
	ELG( *(UInt32*)&scsiCDB.cdb[0], *(UInt32*)&scsiCDB.cdb[4] , '=CDB', "executeCommand - CDB" );

	fpMsgOut = fpMsgPut = &fMsgOutBuffer[ 0 ];

		/* Identify byte:	*/

    msgByte = kSCSIMsgIdentify | kSCSIMsgEnableDisconnectMask | fCurrentTargetLun.lun;
    if ( scsiCDB.cdbFlags & kCDBFlagsNoDisconnect )
         msgByte &= ~kSCSIMsgEnableDisconnectMask;
    *fpMsgOut++ = msgByte;

		/* Tag msg:	*/

    if ( scsiCDB.cdbTagMsg )
    {
        *fpMsgOut++ = scsiCDB.cdbTagMsg;
        *fpMsgOut++ = scsiCDB.cdbTag;
		ELG( 0, scsiCDB.cdbTagMsg<<16 | (scsiCDB.cdbTag & 0xFF), ' tag', "CurioSCSIController::executeCommand - tag" );
    }

		/* Abort msg:	*/

    if ( scsiCDB.cdbAbortMsg )
    {
		ELG( scsiCommand->getOriginalCmd(), scsiCDB.cdbAbortMsg, 'Abor', "CurioSCSIController::executeCommand - abort msg." );
        *fpMsgOut++ = scsiCDB.cdbAbortMsg;
    }

	fMsgOutCount = (UInt8)(fpMsgOut - &fMsgOutBuffer[ 0 ]);
	ELG( fMsgOutCount, *(UInt32*)&fMsgOutBuffer[0], '=MgO', "executeCommand" );

		/***** Try to start the command on the hardware:	*****/

	rc = startCommand();			/* Call the hardware layer.	*/

	if ( rc )
	{								/* Hardware can't start now	*/
		ELG( fCmd, 0, 'Exe-', "CurioSCSIController::executeCommand - command bounced back" );
		fCmd = NULL;		/// ?redundant? - rescheduleCommand done in fsmSelecting
		return;
	}

    return;
}/* end executeCommand */



bool CurioSCSIController::startCommand()
{
	SCSICDBInfo		scsiCDB;
	UInt8			selectCmd;


    fCmd->getPointers( &fCmdData->mdp, &fCmdData->xferCount, &fCmdData->isWrite );

	fCmdData->results.bytesTransferred	= 0;
	fCmdData->savedDataPosition			= 0;

	fCmd->getCDB( &scsiCDB );

	setCmdReg( cFlshFFO );
	WRITE_REGISTER( rSTA, fCurrentTargetLun.target );

		/* Put the contents of the message buffer into the FIFO.		*/
		/* Figure out which flavor of Select command is indicated.		*/
		/* The command depends on the number of message bytes.			*/

	switch ( fMsgOutCount )
	{
	case 0:				/* Select, no ATN, send CDB	    */
	    selectCmd = cSlctNoAtn;
	    break;

	case 1:				/* Select, ATN, 1 Msg, send CDB */
	    selectCmd = cSlctAtn;
	    break;

	case 3:				/* Select, ATN, 3 Msg, send CDB */
	    selectCmd = cSlctAtn3;
	    break;

	default:			/* Select, ATN, 1 Msg, stop	*/
	    selectCmd = cSlctAtnStp;
	    break;
	}/* end SWITCH on size of MsgOut */

	if ( (fMsgOutCount > 3) || (fMsgOutCount + scsiCDB.cdbLength >= 16) )
	{		/* The FIFO only holds 16 bytes.							*/
			/* We have to do the command phase in the state automaton.	*/
		WRITE_REGISTER( rFFO, *fpMsgPut++ );
	    selectCmd = cSlctAtnStp;
	}
	else
	{		/* MsgOut and CDB both fit in FIFO:	*/
	    while ( fpMsgPut < fpMsgOut )
		{
			WRITE_REGISTER( rFFO, *fpMsgPut++ );
		}
	}

	if ( selectCmd != cSlctAtnStp )
	{		/* If we're not stopping after the messages,	*/
			/* stuff the command into the FIFO, too.		*/
	    putCDBIntoFIFO();
	}
		/* Init the finite state automaton:	*/

	fBusState			= SCS_SELECTING;
	fCurrentBusPhase	= kBusPhaseBusFree;

		/***** Issue the Arbitrate/Select/MessageOut combo:	*****/

	setCmdReg( selectCmd );

	return false;					/* return No Problem		*/
}/* end startCommand */


void CurioSCSIController::completeCommand()
{
	ELG( fCmdData->results.returnCode, fCmdData->results.bytesTransferred, ' IOC', "CurioSCSIController::completeCommand" );

	switch ( fCmdData->results.scsiStatus )
	{
	case kSCSIStatusGood:
		break;

	case kSCSIStatusCheckCondition:
		ELG( 0, 0, 'Chek', "CurioSCSIController::completeCommand - Check Condition" );
		fCmdData->results.returnCode = kIOReturnError;
		break;

	default:
		ELG( fCmd, fCmdData->results.scsiStatus, 'Sta?', "CurioSCSIController::completeCommand - bad status" );
		fCmdData->results.returnCode = kIOReturnError;
		break;
	}/* end SWITCH on SCSI status */

    fCmd->setResults( &fCmdData->results );
    fCmd->complete();

	fCmd						= NULL;
	fCmdData					= NULL;
	fCurrentTargetLun.target	= kInvalidTarget;
	fCurrentTargetLun.lun		= kInvalidLUN;
	return;
}/* end completeCommand */


void CurioSCSIController::cancelCommand( IOSCSICommand *scsiCommand )
{
    IOSCSICommand	*origCmd;
 	PrivCmdData		*origCmdData;
	SCSIResults		results;

    origCmd		= scsiCommand->getOriginalCmd();
    origCmdData	= (PrivCmdData*)origCmd->getCommandData();

	ELG( scsiCommand, origCmd, 'Can-', "CurioSCSIController::cancelCommand" );

	origCmd->getResults( &results );
	results.bytesTransferred = origCmdData->results.bytesTransferred;
	origCmd->setResults( &results );

    origCmd->complete();

    scsiCommand->complete();
	return;
}/* end cancelCommand */


void CurioSCSIController::resetCommand( IOSCSICommand *scsiCommand )
{
	ELG( scsiCommand, 0, 'Rst-', "CurioSCSIController::resetCommand" );
	resetBus();
	fCmdData = scsiCommand->getCommandData();
	bzero( &fCmdData->results, sizeof( fCmdData->results ) );
	scsiCommand->setResults( &fCmdData->results );
    scsiCommand->complete();
	return;
}/* end resetCommand */


    /* Fetch the device's bus address and interrupt port number.  */
    /* Also, allocate one page of memory for the Channel Program. */

IOReturn CurioSCSIController::initHardware()
{
    IOReturn        ioReturn;


	fInitiatorID		= kInitiatorIDDefault;
	fInitiatorIDMask	= 1 << kInitiatorIDDefault;	/* BusID bitmask for reselection. */

	ELG( 0, fInitiatorID, 'IniH', "initHardware" );

	ioReturn = getHardwareMemoryMaps();

    if ( ioReturn == kIOReturnSuccess )	ioReturn = allocHdwAndChanMem();
    if ( ioReturn == kIOReturnSuccess )	ioReturn = doHBASelfTest();
    if ( ioReturn == kIOReturnSuccess )	ioReturn = resetHardware( true );

    if ( ioReturn != kIOReturnSuccess )
        this->free();

    return ioReturn;
}/* end initHardware */


IOReturn CurioSCSIController::getHardwareMemoryMaps()
{

	if ( !fSCSIMemoryMap )
	{
	    fSCSIMemoryMap = fProvider->mapDeviceMemoryWithIndex( kCurioRegisterBase );
	    if ( !fSCSIMemoryMap )
		{
			ELG( 0, 0, 'Map-', "CurioSCSIController::getHardwareMemoryMaps - can't map Curio." );
			return kIOReturnInternalError;
	    }

		fCurioPhysAddr	= fSCSIMemoryMap->getPhysicalAddress();
		fCurioAddr		= (UInt8*)fSCSIMemoryMap->getVirtualAddress();
		ELG( fCurioPhysAddr, fCurioAddr, '=Cur', "getHardwareMemoryMaps - MESH regs" );
		g.curioAddr = (UInt32)fCurioAddr;      // for debugging, miniMon ...
	}

	if ( !fDBDMAMemoryMap )
	{
	    fDBDMAMemoryMap	= fProvider->mapDeviceMemoryWithIndex( kDBDMARegisterBase );
	    if ( !fDBDMAMemoryMap )
		{
			ELG( 0, 0, 'map-', "CurioSCSIController::getHardwareMemoryMaps - can't map DBDMA." );
			return kIOReturnInternalError;
	    }
		fDBDMAAddrPhys	= fDBDMAMemoryMap->getPhysicalAddress();
		fDBDMAAddr		= (IODBDMAChannelRegisters*)fDBDMAMemoryMap->getVirtualAddress();
		ELG( fDBDMAAddrPhys, fDBDMAAddr, '=DMA', "getHardwareMemoryMaps - DBDMA regs" );
#if CustomMiniMon
		gMESH_DBDMA      = (UInt32)fDBDMAAddr;
		gMESH_DBDMA_Phys = (UInt32)fDBDMAAddrPhys;
#endif /* CustomMiniMon */
	}

	return kIOReturnSuccess;
}/* end getHardwareMemoryMaps */



    /* Fetch the device's bus address and allocate one page of memory   */
    /* for the channel command. (Strictly speaking, we don't need an    */
    /* entire page, but we can use the rest of the page for a permanent */
    /* status log).                                                     */
    /* @param   deviceDescription   Specify the device to initialize.   */
    /* @return  kIOReturnSuccess if successful, else an error status.       */

IOReturn CurioSCSIController::allocHdwAndChanMem()
{
    IOReturn    ioReturn = kIOReturnSuccess;


    fCCLSize  = page_size;
    fCCL      = (UInt8*)kalloc( fCCLSize );
    if ( !fCCL )
    {   PAUSE( 0, fCCLSize, 'CCA-', "allocHdwAndChanMem - can't allocate channel command area.\n" );
        ioReturn = kIOReturnNoMemory;
    }

    if ( ioReturn == kIOReturnSuccess )
    {
            /* Get the physical address corresponding the DBDMA channel area:   */

		fCCLPhysAddr = pmap_extract(	kernel_pmap,
										(vm_offset_t)fCCL );

		g.cclPhysAddr   = (UInt32)fCCLPhysAddr;  // for debugging ease
		g.cclLogAddr    = (UInt32)fCCL;
        if ( ioReturn != kIOReturnSuccess )
            PAUSE( 0, ioReturn, 'MAP-', "allocHdwAndChanMem - DBDMA mapping err.\n" );
    }

    if ( ioReturn == kIOReturnSuccess )
    {
        ELG( fCCLPhysAddr, fCCL, '=CCL', "allocHdwAndChanMem - CCL phys/logical addresses." );

			/* Set the Interrupt, Branch, and Wait DBDMA registers.		*/
			/* Caution: the following Curio interrupt register bits		*/
			/* are reverse polarity and are in a different position.	*/
			/* The pattern is: 0x00MM00VV, where MM is a mask byte		*/
			/* and VV is a value byte to match.							*/
			/*  0x80    means NO errors			(kMeshIntrError)		*/
			/*  0x40    means NO exceptions	    (kMeshIntrException)	*/
			/*  0x20    means NO command done   (kMeshIntrCmdDone)		*/
			/* Branch Select is used with BRANCH_FALSE					*/

	//	IOSetDBDMAInterruptSelect( fDBDMAAddr, 0x00000000);	/* Never let DBDMA interrupt	*/
	//	IOSetDBDMAWaitSelect(      fDBDMAAddr, 0x00200020);	/* Wait until command done		*/
	//	IOSetDBDMABranchSelect(    fDBDMAAddr, 0x00000000);	/* Never branch on error		*/
		fDBDMAAddr->interruptSelect = 0x00000000;
		fDBDMAAddr->waitSelect		= 0x20002000;
		fDBDMAAddr->branchSelect	= 0x00000000;
	    SynchronizeIO();
    }
        /* What do we do on failure? Should we try to deallocate    */
        /* the stuff we created, or will the system do this for us? */

    return  ioReturn;
}/* end allocHdwAndChanMem */


	/* doHBASelfTest - This should be extended to perform a real chip self-test.	*/

IOReturn CurioSCSIController::doHBASelfTest()
{
	IOReturn		ioReturn	= kIOReturnSuccess;
	UInt8			chipID;
	UInt8			temp;


    ELG( fCurioPhysAddr, fCurioAddr, 'Test', "doHBASelfTest" );
	resetCurio();

	WRITE_REGISTER( rCF2, CF2_EPL );
	setCmdReg( cNOP | bDMAEnBit );

	chipID = fCurioAddr[ rTCH ];

		/* Check data integrity by writing a NOP to the command register	*/
		/* and verifying that it was read back correctly.					*/

	setCmdReg( cNOP );
	temp = fCurioAddr[ rCMD ];
	if ( temp != cNOP )
	{
		PAUSE( cNOP, temp, 'hba-', "doHBASelfTest - Expected NOP" );
		ioReturn = kIOReturnNoDevice;
	}
		/* To do: write a test pattern into the FIFO and	*/
		/* check for correct count and bits.				*/

	return ioReturn;
}/* end doHBASelfTest */


void CurioSCSIController::interruptOccurred( IOInterruptEventSource *ies, int intCount )
{
//	DBDMAChannelRegisters	*DBDMARegs = (DBDMAChannelRegisters*)fDBDMAAddr;


//	ELG( ies, intCount, 'Int+', "interruptOccurred" );

	g.intLevel |=  kLevelISR;                               /* set ISR flag     */
	g.intLevel &= ~kLevelLatched;                           /* clear latched    */

//	ELG( DBDMARegs->channelStatus, DBDMARegs->commandPtrLo, 'Int+', "interruptOccurred." );
	ELG( fCmd, 0, 'Int+', "interruptOccurred." );
//  ELG( *(UInt32*)0xF3000024, *(UInt32*)0xF300002C, 'Int ', "interruptOccurred." );

	doHardwareInterrupt();						/**** HANDLE THE INTERRUPT  ****/

//  ELG( fCmd, *(UInt32*)0xF300002C, 'Intx', "interruptOccurred." );

    g.intLevel &= ~kLevelISR;                  /* clear ISR flag    */
    return;
}/* end interruptOccurred */



void CurioSCSIController::doHardwareInterrupt()
{
	if ( !interruptPending() )
	{
		ELG( 0, 0, 'Int?', "CurioSCSIController::doHardwareInterrupt - spurious interrupt" );
		return;
	}

	fCkForAnotherInt = true;	// keep going around
	do
	{
		ELG( fCkForAnotherInt<<16 | fNeedAnotherInterrupt, fBusState, 'loop', "doHardwareInterrupt" );
		fNeedAnotherInterrupt = false;

			/* Gross error is set incorrectly (according to Clinton Bauder).	*/

		if ( fSaveStatus & sGrossErr )	// ??? mlj was fSaveInterrupt
		{
			ELG( 0, fSaveInterrupt, 'GEr-', "doHardwareInterrupt - Gross Error set." );
			fSaveStatus &= ~sGrossErr;
		}

				/* iIlegalCmd - Software messed up.	*/
				/* Start over from scratch.			*/
				/* We can get this if we write too	*/
				/* many commands into the register.	*/

		if ( fSaveInterrupt & iIlegalCmd )
		{
			ELG( 0, fSaveInterrupt, 'IllC', "doHardwareInterrupt - Illegal command interrupt" );
			if ( fBusState != SCS_DEATH_MARCH )
				fsmStartErrorRecovery( kIOReturnInternalError );

			fCkForAnotherInt = false;
		}/* end IF iIlegalCmd */


			/* Check for disconnect:	*/

		if ( fSaveInterrupt & iDisconnect )
		{
			setCmdReg( cEnSelResel );
				/* Radar 1678545: this is the only (normal) place	*/
				/* that fBusBusy is cleared. (It's also cleared by	*/
				/* bus reset and driver initialization.)			*/
			fBusBusy = false;
		}

		if ( fSaveInterrupt & iResetDetect )
		{
			ELG( fSaveStatus, fSaveInterrupt, 'BusR', "doHardwareInterrupt - SCSI Bus Reset\n" );
			if ( fCmd )
			{
				fCmdData->results.returnCode = kIOReturnSelectionError;
				completeCommand();
			}
			super::resetOccurred();
			enableCommands();			/* let superclass issue another command	*/
			fCkForAnotherInt = false;
		}/* end IF iResetDetect */
		else if ( (fSaveStatus & sParityErr) && fBusBusy )
		{
			fsmStartErrorRecovery( kIOReturnParityError );
			fCkForAnotherInt = false;
		} /* If Parity Error and not disconnected */
		else
		{		/* Only certain states are legal if the bus is busy.	*/
			if ( fBusBusy )
			{
					/* This is a legitimate interrupt (parity error and	*/
					/* bus reset have already been handled). The entity	*/
					/* that started the chip action that caused the		*/
					/* interrupt (deep breath) set fBusState to indicate*/
					/* why the interrupt happened.						*/
					/* Call the proper finite-state machine function.	*/
					/* On return, fBusState will be set to one of the	*/
					/* following values:								*/
					/*	SCS_INITIATOR			Start an action on the	*/
					/* 							current phase.			*/
					/*	SCS_DISCONNECT			The bus is free.		*/
					/*	SCS_WAIT_FOR_BUS_FREE	The bus should go free	*/
					/*							shortly.				*/
					/* Note that SCS_WAIT_FOR_BUS_FREE and				*/
					/* fsmWaitForBusFree short-circuit some of the		*/
					/* automaton to avoid unnecessary interrupt events.	*/
				switch ( fBusState )
				{
				case SCS_DISCONNECTED:		fsmDisconnected();	break;
				case SCS_SELECTING:			fsmSelecting();		break;
				case SCS_RESELECTING:		fsmReselecting();	break;
				case SCS_INITIATOR:			fsmInitiator();		break;
				case SCS_COMPLETING:		fsmCompleting();	break;
				case SCS_DMACOMPLETE:		fsmDMAComplete();	break;
				case SCS_SENDINGMSG:							break;
				case SCS_GETTINGMSG:		fsmGettingMsg();	break;
				case SCS_SENDINGCMD:		fsmSendingCmd();	break;
				case SCS_WAIT_FOR_BUS_FREE:	fsmWaitForBusFree();break;
				case SCS_DEATH_MARCH:		fsmErrorRecoveryInterruptService(); break;

				default:
					PAUSE( fSaveStatus, fBusState, 'FSM-', "doHardwareInterrupt - Illegal bus automaton state." );
				}/* end SWITCH fBusState */
			}/* end IF bus is busy */
			else
			{		/* The bus is (or just went) free.					*/
					/* We only allow selection, reselection,			*/
					/* wait for disconnect, or death march interrupts.	*/
				switch ( fBusState )
				{
				case SCS_DISCONNECTED:		fsmDisconnected();		break;
				case SCS_SELECTING:			fsmSelecting();			break;
				case SCS_WAIT_FOR_BUS_FREE:	fsmWaitForBusFree();	break;
				case SCS_DEATH_MARCH:		fsmErrorRecoveryInterruptService(); break;

				default:
					 fsmStartErrorRecovery(	kIOReturnInternalError );
					 fCkForAnotherInt = false;
					 break;
				}/* end SWITCH on fBusState */
			}/* end ELSE bus is not busy */
		}

			/* We have (presumably) completely handled the previous		*/
			/* interrupt. At this point, there are five legitimate		*/
			/* fBusState values:										*/
			/*	SCS_RESELECTING		Just got a reselection interrupt	*/
			/*	SCS_INITIATOR		Continue operation for this target.	*/
			/*	SCS_DISCONNECTED	Enable selection/reselection		*/
			/*	SCS_WAIT_FOR_BUS_FREE	Command completion transition	*/
			/*	SCS_DEATH_MARCH		Error recovery						*/
			/*  Handle a SCSI Phase change if necessary. This will		*/
			/* leave the bus state in the "expected" state for the next	*/
			/* operation.												*/

		switch ( fBusState )
		{
		case SCS_DEATH_MARCH:
			fCkForAnotherInt = false;
			break;

		case SCS_INITIATOR:
			ELG( fCkForAnotherInt<<16 | fNeedAnotherInterrupt, 0, 'SCSi', "doHardwareInterrupt" );
			if ( fNeedAnotherInterrupt )
			{		// from tagged reselect
				fNeedAnotherInterrupt = false;
				if ( quickCheckForInterrupt() )
				{
					fsmPhaseChange();
				}
			}
			else
			{
				fsmPhaseChange();
			}
			break;

		case SCS_WAIT_FOR_BUS_FREE:
		default:
			break;
		}/* end SWITCH on fBusState */

			/* This is the final check: if we determine that the	*/
			/*  previous action will complete quickly (for example,	*/
			/* it's a message in byte), we'll spin for up to ten	*/
			/* microseconds to see if the chip is ready for another	*/
			/* operation.											*/

	} while ( fCkForAnotherInt && quickCheckForInterrupt() );

	if ( fBusState == SCS_DEATH_MARCH )
		fsmContinueErrorRecovery();

	if ( fBusState == SCS_DISCONNECTED )
		enableCommands();		/* Try to start another command	*/

	return;
}/* end doHardwareInterrupt */


	/* This is called from fsmDisconnected when we receive a reselected interrupt.	*/

void CurioSCSIController::handleReselectionInterrupt()
{
	UInt8			selectByte;
	UInt32			fifoDepth;
	UInt8			msgByte;
	IOReturn		ioReturn = kIOReturnSuccess;


		/* Make sure there's a selection byte and an Identify message in the FIFO.	*/

	fBusBusy	= ((fSaveInterrupt & iDisconnect) == 0);
	fifoDepth	= fCurioAddr[ rFOS ] & kFIFOCountMask;

	ELG( fifoDepth, fSaveInterrupt, 'fHRI', "handleReselectionInterrupt" );
	if ( fifoDepth != 2 )
	{
	    ioReturn	= kIOReturnInternalError;
		PAUSE( 0, 0, 'Rsl-', "handleReselectionInterrupt - Bad FIFO count on reselect" );
	}

	if ( ioReturn == kIOReturnSuccess )
	{
	    selectByte = fCurioAddr[ rFFO ];
			/* During reselection, the first message byte	*/
			/* must be an Identify with the LUN.			*/
	    ioReturn = getReselectionTargetID( selectByte );
	}

	if ( ioReturn == kIOReturnSuccess )
	{
	    msgByte = fCurioAddr[ rFFO ];		/* Pull the Identify byte	*/
	    if ( fSaveStatus & sParityErr )
			ioReturn	= kIOReturnParityError;
	}

	if ( ioReturn == kIOReturnSuccess )
	{
	    if ( (msgByte & kSCSIMsgIdentify) == 0 )
		{
			ioReturn = kIOReturnInternalError;
			PAUSE( 0, msgByte, ' ID-', "handleReselectionInterrupt - Bad ID Message (no Identify) on reselect" );
	    }

	    fCurrentTargetLun.lun	= msgByte & ~kSCSIMsgIdentify;
		fTag					= kInvalidTag;
	    fBusState				= SCS_RESELECTING;

			/* At this point, the chip is waiting for us to validate	*/
			/* the Identify message. While we have a Target and LUN,	*/
			/* we don't have a command to reconnect to.					*/
		setCmdReg( cMsgAcep );			/* Accept the Identify message	*/
	}

	if ( ioReturn != kIOReturnSuccess )
	    fsmStartErrorRecovery( ioReturn );

	return;
}/* end handleReselectionInterrupt */


	/* Complete the processing of a reselection interrupt.				*/
	/* We have just ACK'd the Identify message. Try to find the command	*/
	/* that corresponds to this target.lun (without a queue tag).		*/
	/* If we're successful, complete the reselection and start			*/
	/* following phases by calling fsmPhaseChange directly.				*/
	/* If the first disconnected command has a non-zero tag queue, we	*/
	/* expect to be in messsage in phase, and will receive a tagged		*/
	/* queue message eventually.										*/

void CurioSCSIController::fsmReselecting()
{
	ELG( fBusState, *(UInt16*)&fCurrentTargetLun<<16 | (UInt16)fTag, 'fRsl', "fsmReselecting" );

	reselectNexus();
	if ( fCmd )
	{		/* We have successfully reselected this target.		*/
			/* Wait until the target tells us what to do next.	*/
	    fBusState = SCS_INITIATOR;
		return;
	}

		/* We don't have a nexus yet. If we're in MSGI phase,	*/
		/* continue grabbing message bytes until we receive		*/
		/* a queue tag message (we might receive an SDTR),		*/
		/* continuing at fsmReselecting to process interrupts.	*/

	quickCheckForInterrupt();	// clear pending interrupt(s) and update phase
	fCurrentBusPhase = fSaveStatus & mPhase;
	if ( fCurrentBusPhase == kBusPhaseMSGI )
	{
		if ( (fCurioAddr[ rFOS ] & kFIFOCountMask) == 0 )
		{
			if ( fMsgInState == kMsgInInit )
			{
				setCmdReg( cNOP | bDMAEnBit );	// clear TC zero bit
				setCmdReg( cIOXfer );
				if ( !quickCheckForInterrupt() )
				{	fCkForAnotherInt = true;
					return;
				}
			}
		}
		fsmGettingMsg();
	}
	else
	{		/* We're in the wrong phase. Bail out.	*/
		fsmStartErrorRecovery( kIOReturnInternalError );
	}
	return;
}/* end fsmReselecting */



	/* Disconnected - only legal event here is reselection.				*/
	/* handleReselectionInterrupt may set fBusState to SCS_INITIATOR	*/

void CurioSCSIController::fsmDisconnected()
{
	UInt8	selectByte, target;


	ELG( 0, fSaveInterrupt, 'fDis', "fsmDisconnected" );

	if ( fSaveInterrupt & iReselected )
	{
	    handleReselectionInterrupt();
		return;
	}

		/* See if some target trying to Select us - should never happen:	*/

	if ( fSaveInterrupt & (iSelected | iSelectWAtn) )
	{
	    selectByte = fCurioAddr[ rFFO ];
	    selectByte &= ~fInitiatorIDMask;
	    for ( target = 0; target < 8; target++ )
		{
			if ( selectByte & (1 << target) )
				break;
	    }
	    PAUSE( selectByte, target, 'Tar-', "fsmDisconnected - Selected by external target - not supported." );
	    setCmdReg( cDisconnect );
		return;
	}/* end IF bogus selection by target */


	fCkForAnotherInt = false;

	if ( fBusBusy == false )
	{		/* disconnect interrupted while disconnected			*/
			/* This always happens when we finish an IO request.	*/
			/* fBusBusy is cleared when the "bus disconnected" bit	*/
			/* is set in the NCR 53C96 interrupt register.			*/
		ELG( 0, 0, 'Dis-', "fsmDisconnected - disconnect interrupted while disconnected" );
		enableCommands();
		return;
	}

			/* Since we're disconnected, just ignore the interrupt	*/

	if ( fCmd )
	{
		fCmdData->results.returnCode = kIOReturnSelectionError;
		completeCommand();
	}

	fCurrentBusPhase = kBusPhaseBusFree;
	return;
}/* end fsmDisconnected */


	/* This state is called when we expect to disconnect from a		*/
	/* target after receiving a Command Complete, Disconnect, or	*/
	/* Abort message and the bus is still busy on exit from the		*/
	/* finite state automaton (because curioQuickCheckForBusFree	*/
	/* returned false).												*/

void CurioSCSIController::fsmWaitForBusFree()
{
	ELG( fBusBusy, fSaveInterrupt, 'fWBF', "fsmWaitForBusFree" );

	if ( fSaveInterrupt & (iReselected | iSelected | iSelectWAtn) )
	{
			/* We went straight from command completion to (presumably)	*/
			/* reselection. Treat it as a normal selection/reselection	*/
			/* interrupt from "disconnected" state. (This is an abuse of*/
			/* the finite-state automaton design.)						*/
	    fsmDisconnected();
	}
	else if ( fBusBusy == false )
	{
			/* This is expected: the bus has just gone free after a	*/
			/* command completed. We can now safely try to start	*/
			/* another command.										*/
	    fBusState = SCS_DISCONNECTED;
		fCkForAnotherInt = false;
		enableCommands();
	}
	else
	{		/* This is strange. It may not be an error, but I don't know	*/
			/* if there are other legal bus states after disconnect.		*/
			/* (The one counter example would be after a linked command		*/
			/* when the target goes to Command phase, but we should have	*/
			/* rejected this at command complete.)							*/
		ELG( fCmd, 0, 'WBF-', "fsmWaitForBusFree - strangeness" );
	    fsmStartErrorRecovery( kIOReturnInternalError );
	}
	return;
}/* end fsmWaitForBusFree */


	/* fsmSelecting - One of three things can happen here:		*/
	/*		the selection could succeed (though with possible	*/
	/*				incomplete message out),					*/
	/*		it could time out, or								*/
	/*		we can be reselected.								*/

void CurioSCSIController::fsmSelecting()
{
	SCSICDBInfo			scsiCDB;
	UInt8				fifoDepth;
	IOReturn			ioReturn	= kIOReturnSuccess;


	ELG( 0, fSaveInterrupt, 'fSel', "fsmSelecting" );

	fBusBusy = ((fSaveInterrupt & iDisconnect) == 0 );
	if ( fBusBusy == false )
	{		/* Selection timed-out. Abort this request.	*/
		ELG( fCmd, 0, 'Tim-', "fsmSelecting - timeout" );

	    setCmdReg( cFlshFFO );
	    fBusState = SCS_DISCONNECTED;
		fCmdData->results.returnCode = kIOReturnNoDevice;
        completeCommand();
		return;
	}

	ELG( fSaveInterrupt, fSaveSeqStep, 'Sel-', "fsmSelecting - problem other than timeout" );
	fCmd->getCDB( &scsiCDB );

	if ( fSaveInterrupt == (iFuncComp | iBusService) )
	{
	    switch ( fSaveSeqStep & INS_STATE_MASK )
		{
	    case 0:	/* No message phase. If we really wanted one,			*/
				/* this could be significant.							*/
				/* OK, let's try to continue following phase changes.	*/
			fBusState = SCS_INITIATOR;
			break;

	    case 3: /* didn't complete cmd phase, parity?	*/
	    case 4: /* everything worked					*/
	    case 1: /* everything worked, SCMD_SELECT_ATN_STOP case 				*/
				/* We're connected. Start following the target's phase changes.	*/
				/* If we're trying to do sync negotiation,						*/
				/* this is the place to do it. In that case, we					*/
				/* sent a SCMD_SELECT_ATN_STOP command, and						*/
				/* ATN is now asserted (and we're hopefully in					*/
				/* msgOut phase). We want to send 5 bytes.						*/
				/* Drop them into currMsgOut									*/
			fBusState = SCS_INITIATOR;
			break;

	    case 2:		/* Either no command phase, or incomplete message transfer.	*/
			fifoDepth = fCurioAddr[ rFOS ] & kFIFOCountMask;
				/* Spec says ATN is asserted if all message bytes were not sent.	*/
			if ( fifoDepth > scsiCDB.cdbLength )
				setCmdReg( cRstAtn );

				/* 970611: clear the FIFO. If we don't do this and the	*/
				/* target is entering MSGI phase (trying to send us a	*/
				/* Message Reject), it will sit on top of the FIFO, and	*/
				/* we'll read garbage from the FIFO.					*/

			setCmdReg( cFlshFFO );	/* The command is still in the FIFO */
				/* OK, let's try to continue following phase changes.	*/
			fBusState = SCS_INITIATOR;
			break;

	    default:	/* fBusBusy = true;	    -- Radar 1678545 */
			ioReturn = kIOReturnInternalError;
			break;
	    }/* end SWITCH */
	}
	else if ( fSaveInterrupt & iReselected )
	{
			/* We were reselected while trying to do a selection.		*/
			/* Enqueue this cmdBuf on the HEAD of pendingQ, then deal	*/
			/* with the reselect.										*/
			/* Tricky case, we have to "deactivate" this command		*/
			/* since this hardwareStart attempt failed.					*/

		ELG( fCmd, fSaveInterrupt, 'Arb-', "fsmSelecting - Reselection interrupt while selecting" );
		disableCommands();
		rescheduleCommand( fCmd );
		fCmd = NULL;

	    fBusState = SCS_DISCONNECTED;
	    handleReselectionInterrupt();		/* Go deal with reselect.	*/
	}
	else
	{
		ioReturn = kIOReturnInternalError;
		PAUSE( 0, 0, 'int-', "Bogus select/reselect interrupt" );
	}

	if ( ioReturn != kIOReturnSuccess )
	    fsmStartErrorRecovery( ioReturn );

	return;
}/* end fsmSelecting */


	/* This is a dummy interrupt service state that we enter after selection	*/
	/* when the previous Curio operation has no interrupt-service completion	*/
	/* requirement. For example, we can get here after a Message Out or			*/
	/* reselection. Nothing happens here; we continue at fsmPhaseChange.		*/

void CurioSCSIController::fsmInitiator()
{
	ELG( 0, fSaveInterrupt, 'fIni', "fsmInitiator" );
	fBusState = SCS_INITIATOR;
	return;
}/* end fsmInitiator */


	/* We just did a SCMD_INIT_CMD_CMPLT command, hopefully all that's left		*/
	/* is to drop ACK. Command Complete message is handled in fscAcceptinfMsg.	*/
	/* We can't go to SCS_DISCONNECTED until the target disconnects. If we		*/
	/* go to "disconnected" state too soon, we'll encounter a load-dependent	*/
	/* race condition that causes us to start another command before we've		*/
	/* cleaned up from the last command. The actual state change is in			*/
	/* fsmProcessMessage.														*/

void CurioSCSIController::fsmCompleting()
{
	unsigned		fifoDepth;
	IOReturn		ioReturn	= kIOReturnSuccess;
	UInt8			statusByte;


	ELG( 0, fSaveInterrupt, 'fCmp', "fsmCompleting" );

	if ( fSaveInterrupt & iDisconnect )
	{
	    ioReturn = kIOReturnInternalError;
		PAUSE( 0, 0, ' BF-', "fsmCompleting - Bus free before target completion complete" );
	}

	if ( ioReturn == kIOReturnSuccess )
	{
	    fifoDepth = fCurioAddr[ rFOS ] & kFIFOCountMask;
	    if ( fSaveInterrupt & iFuncComp )
		{		/* Got both Status and MsgIn in FIFO; ACK is still asserted.	*/
			if ( fifoDepth != 2 )
			{		/* This is pretty bogus - we expect a status and msg in the FIFO.	*/
				PAUSE( 0, fifoDepth, 'ffc-', "fsmCompleting - FIFO problem with Status/MsgIn" );
				ioReturn = kIOReturnInternalError;
			}

			if ( ioReturn == kIOReturnSuccess )
			{
				statusByte		= fCurioAddr[ rFFO ];
				fMsgInBuffer[0]	= fCurioAddr[ rFFO ];
				fCmdData->results.scsiStatus = statusByte;
				fMsgInIndex	    = 1;
				ioReturn	    = fsmProcessMessage();
			}
	    }
	    else
		{		/* We only received a status byte.								*/
				/* This can occur if we responded to the interrupt before		*/
				/* the device successfully transmitted the Command Complete		*/
				/* message. This is kind of weird, but let's try to handle it.	*/
			if ( fifoDepth != 1 )
			{
				ioReturn = kIOReturnInternalError;
				PAUSE( 0, fifoDepth, 'Cmp-', "IO complete: incorrect FIFO count (expecting one byte)" );
			}

				/* Back to watching phase changes.						*/
				/* Presumably, the target will switch to MSGI phase and	*/
				/* complete the command at its leisure.					*/
			fBusState = SCS_INITIATOR;
	    }
	}

	if ( ioReturn != kIOReturnSuccess )
	    fsmStartErrorRecovery( ioReturn );

	return;
}/* end fsmCompleting */


	/* DMA Complete.	*/

void CurioSCSIController::fsmDMAComplete()
{
	UInt32		fifoResidualCount, actualTransferCount, dmaResidualCount;
	UInt8		residualByte;


		/* Stop the DMA engine and retrieve the total number of		*/
		/* bytes transferred. This will be							*/
		/* fCmd->thisTransferLength	The number of bytes requested	*/
		/*	- the current DMA residual count						*/
		/*	- the current FIFO residual count						*/
		/* Note that there may still be bytes in the FIFO.			*/

//	IODBDMAReset( fDBDMAAddr );
	fDBDMAAddr->channelControl = SWAP( 0x20002000 );		// set FLUSH bit
		SynchronizeIO();
	fDBDMAAddr->channelControl = SWAP( 0x80000000 );		// clr RUN   bit
		SynchronizeIO();

		/* Get residual count from DMA transfer.	*/

	dmaResidualCount	= fCurioAddr[ rXCM ] << 8 | fCurioAddr[ rXCL ];
	actualTransferCount	= fThisTransferLength - dmaResidualCount;

	ELG( dmaResidualCount, actualTransferCount, 'fDMA', "fsmDMAComplete" );

		/* Now, see if there are any bytes lurking in the FIFO:	*/

	fifoResidualCount = fCurioAddr[ rFOS ] & kFIFOCountMask;

	if ( fifoResidualCount > 0 )
	{		/* Drain the FIFO after DMA for a normal transfer:	*/
		ELG( 0, fifoResidualCount, 'MTFF', "fsmDMAComplete - empty the FIFO" );

	    if ( !fCmdData->isWrite )
		{
			while ( fifoResidualCount > 0 )
			{		/* Try to move residual data from the FIFO into	*/
					/* the user's buffer.							*/
				residualByte = fCurioAddr[ rFFO ];
				fCmdData->mdp->writeBytes( fCmdData->results.bytesTransferred + actualTransferCount,
						&residualByte, 1 );
				++actualTransferCount;
			}/* end WHILE bytes in FIFO */
	    }
	    else
		{		/* Write command - just flush the data */
			setCmdReg( cFlshFFO );
			setCmdReg( cNOP | bDMAEnBit );	// clear TC zero bit;
			actualTransferCount	-= fifoResidualCount;
	    }/* end IF Write */
	}/* end IF bytes left in FIFO */

	fCmdData->results.bytesTransferred += actualTransferCount;

	if ( fSaveStatus & sParityErr )
	{
		PAUSE( 0, 0, 'par-', "fsmDMAComplete - parity error" );
	    fsmStartErrorRecovery( kIOReturnParityError );
	}
	else
	{
	    fBusState = SCS_INITIATOR;		/* Continue following phase changes.	*/
	}

	return;
}/* end fsmDMAComplete */


	/* Just completed the SCMD_TRANSFER_INFO operation for message in.	*/
	/* ACK is not asserted (we have not ACK'ed this byte).				*/
	/* There is no parity error.										*/
	/* We will not have a command if we're reselecting.					*/

void CurioSCSIController::fsmGettingMsg()
{
	UInt32		    fifoCount;
	UInt8		    msgInByte;
	IOReturn	    ioReturn = kIOReturnSuccess;


	ELG( fBusBusy, fMsgInState<<16 | fCurioAddr[ rFOS ], 'fGtM', "fsmGettingMsg" );

	if ( fBusBusy == false )
	{
	    	/* This (non-fatal) error is handled on return...	*/
	    ioReturn = kIOReturnInternalError;	/* Any non-zero non-fatal error status	*/
	}
	else
	{
		fifoCount = fCurioAddr[ rFOS ] & kFIFOCountMask;
	    if ( fifoCount != 1 )
		{
			PAUSE( 0, fifoCount, 'GtM-', "fsmGettingMsg - MsgIn FIFO count error" );
			ioReturn = kIOReturnInternalError;
	    }
	}

	if ( ioReturn == kIOReturnSuccess )
	{
	    msgInByte = fCurioAddr[ rFFO ];
		ELG( 0, msgInByte, '=Msg', "fsmGettingMsg - got a byte" );
	    if ( fSaveStatus & sParityErr )
		{
			PAUSE( 0, fSaveStatus, 'Par-', "fsmGettingMsg - Parity error getting MsgIn" );
			ioReturn = kIOReturnParityError;
	    }
	}

	if ( ioReturn == kIOReturnSuccess )
	{
	    if ( fMsgInState == kMsgInInit )
		{
			fMsgInCount = 0;
			fMsgInIndex = 0;
	    }
	    if ( fMsgInIndex >= kMessageInBufferLength )
		{
			PAUSE( 0, fMsgInIndex, 'Par-', "fsmGettingMsg -  too many bytes" );
			ioReturn = kIOReturnInternalError;
	    }
	}

	if ( ioReturn == kIOReturnSuccess )
	{
	    fMsgInBuffer[ fMsgInIndex++ ] = msgInByte;
	    switch ( fMsgInState )
		{
		case kMsgInInit:				/* This is the first message byte.		*/
			if ( (msgInByte == kSCSIMsgCmdComplete)
			  || (msgInByte >= (UInt8)kSCSIMsgIdentify) )
			{		 /* This is 1-byte cmdComplete or Identify message.				*/
				fMsgInState = kMsgInReady;
			}
			else if ( msgInByte == kSCSIMsgExtended )
            {       /* This is an extended message. The next byte has the count.	*/
                fMsgInState = kMsgInCounting;
            }
			else if ( msgInByte <= kSCSIMsgOneByteMax )
			{		/* These are other 1-byte messages.	*/
				fMsgInState = kMsgInReady;
			}
            else if ( msgInByte >= kSCSIMsgTwoByteMin
                  &&  msgInByte <= kSCSIMsgTwoByteMax )
            {		/* This is a two-byte message.					*/
                    /* Set the count and read the next byte.		*/
                fMsgInState = kMsgInReading;	/* Need one more	*/
                fMsgInCount = 1;
            }
            else
            {       /* This is an unknown message. */
                fMsgInState = kMsgInReady;
            }
            break;

		case kMsgInCounting:        /* Count byte of multi-byte message:   */
            fMsgInCount = msgInByte;
            fMsgInState = kMsgInReading;
            break;

		case kMsgInReading:                 /* Body of multi-byte message:  */
			if ( --fMsgInCount <= 0 )
				fMsgInState = kMsgInReady;
			break;

		default:
			PAUSE( 0, 0, 'Msg-', "DoMessageInPhase  - Bogus MSGI state!\n" );
			ioReturn = kIOReturnInternalError;
	    }/* SWITCH on message state */
	}

	if ( ioReturn == kIOReturnSuccess )
	{
	    switch ( fMsgInState )
		{
	    case kMsgInReading:
	    case kMsgInCounting:	/* We have more message bytes to read.			*/
								/* Accept this byte (this sets ACK) and setup	*/
								/* to transfer the next byte.					*/
			setCmdReg( cMsgAcep );
				/* Since the message accept command sets "interrupt",	*/
				/* eat it here so we don't get a second interrupt.		*/
			quickCheckForInterrupt();
			setCmdReg( cNOP );
			setCmdReg( cIOXfer );
			if ( fBusState != SCS_RESELECTING )
				 fBusState  = SCS_GETTINGMSG;
				/* This would be a good place to spin-wait for completion,	*/
				/* continuing at the start of this method if there is		*/
				/* another byte (and we're still in message in phase).		*/
				/* Perhaps this method should return a "spinwait" status to	*/
				/* the mainline FSM.										*/
			break;

	    case kMsgInReady:
			fMsgInState	= kMsgInInit;
			ioReturn	= fsmProcessMessage();
			break;

	    case kMsgInInit:
	    default:
				/* that's strange: we should never be in idle state	*/
				/* *after* successfully reading a message byte.		*/
			ioReturn = kIOReturnInternalError;
			break;
	    }/* end SWITCH */
	}

	if ( ioReturn != kIOReturnSuccess )
	    fsmStartErrorRecovery( ioReturn );

	return;
}/* end fsmGettingMsg */


	/* Just read a message; ACK is false and the message			*/
	/* has been read into fMsgInBuffer[0..fMsgInIndex]. We have not	*/
	/* ACK'ed the last byte yet.									*/
	/* If we fail here, the caller will start error recovery.		*/

IOReturn CurioSCSIController::fsmProcessMessage()
{
	IOReturn	    ioReturn			= kIOReturnSuccess;
	bool		    messageAckNeeded	= true;


    ELG( fBusState, *(UInt32*)fMsgInBuffer, 'fMgI', "fsmProcessMessage" );

		/* Message-in complete. Handle message(s) in currMsgIn:	*/

	if ( fBusState == SCS_RESELECTING )
	{
	    	/* The only interesting message here is queue tag.	*/
			/* (What about target SDTR or Abort?)				*/

	    switch ( fMsgInBuffer[0] )
		{
	    case kSCSIMsgHeadOfQueueTag:
	    case kSCSIMsgOrderedQueueTag:
	    case kSCSIMsgSimpleQueueTag:
			if ( fMsgInIndex != 2 )
			{
				PAUSE( fMsgInBuffer[0], fMsgInIndex, 'Que-', "fsmProcessMessage - queue tag without tag value" );
				ioReturn = kIOReturnInternalError;
			}
			else
			{	fTag = fMsgInBuffer[ 1 ];
				ELG( 0, fTag, '=Tag', "fsmProcessMessage - tag" );
				reselectNexus();
				if ( fCmd )
				{
					setCmdReg( cMsgAcep );
					fNeedAnotherInterrupt   = true;
					fBusState				= SCS_INITIATOR;
					return kIOReturnSuccess;
				}
			}
			break;

	    default:
			ioReturn = kIOReturnInternalError;
			break;
	    }/* end SWITCH on message */

			/* Since we process commands one by one, whack this			*/
			/* command so we fall through the regular message handler.	*/
	    fMsgInBuffer[0] = kSCSIMsgNop;
	    fMsgInIndex = 1;
	}

	fBusState		    	= SCS_INITIATOR;
	fNeedAnotherInterrupt   = true;

	switch ( fMsgInBuffer[0] )
	{
	case kSCSIMsgNop:
	    break;

	case kSCSIMsgCmdComplete:
			/* Normally, we get here from fsmCommandComplete.			*/
			/* All we need to do is to Ack the message and complete the	*/
			/* command. Exit in a transitional bus state that becomes	*/
			/* SCS_DISCONNECTED when the bus is no longer busy.			*/
	    fBusState = SCS_WAIT_FOR_BUS_FREE;
		completeCommand();
	    break;

	case kSCSIMsgDisconnect:
		fCmd = NULL;
			/* Exit the interrupt service routine so	*/
			/* we don't miss a reselection interrupt.	*/
	    fBusState = SCS_WAIT_FOR_BUS_FREE;
	    fCkForAnotherInt = false;
	    break;

	case kSCSIMsgSaveDataPointers:
        if ( fCmd )
			fCmdData->savedDataPosition = fCmdData->results.bytesTransferred;
	    break;

	case kSCSIMsgRestorePointers:
        if ( fCmd )
			fCmdData->results.bytesTransferred = fCmdData->savedDataPosition;
	    break;

	case kSCSIMsgRejectMsg:
		ioReturn = kIOReturnInternalError;

	//	don't break - fall through to kSCSIMsgAbort	*/

	case kSCSIMsgAbort:
	case kSCSIMsgAbortTag:
		if ( fCmd )
		{		/* Oops: something is terribly wrong with this command.	*/
				/* This can happen if we get a parity error, set ATN,	*/
				/* and send an inititor detected error to the target.	*/
			fCmdData->results.returnCode = kIOReturnInternalError;
			completeCommand();
			fCkForAnotherInt = false;
	    }
		    /* After receiving an Abort, the target will go free	*/
		fBusState = SCS_WAIT_FOR_BUS_FREE;
	    break;

	case kSCSIMsgLinkedCmdComplete:
	case kSCSIMsgLinkedCmdCompleteFlag:
			/* These are impossible: we reject commands with the link bit set.	*/
			/* About all we can do is to fail the client's command and			*/
			/* stagger onwards.													*/
		PAUSE( fCmd, 0, 'Abo-', "fsmProcessMessage - recv'd Abort" );	
		fCmdData->results.returnCode = kIOReturnInternalError;
		completeCommand();
	    fBusState = SCS_WAIT_FOR_BUS_FREE;
	    break;

	case kSCSIMsgExtended:
			/* The only expected extended message is			*/
			/* synchronous negotiation which isn't supported.	*/
	    switch ( fMsgInBuffer[2] )
		{
	    case kSCSIMsgSyncXferReq:
				/* We don't support SDTR (yet), so send a message reject	*/
				/* Perhaps it would be better to send an "async only"		*/
				/* response, but message reject is legal.					*/
			WRITE_REGISTER( rFFO, kSCSIMsgRejectMsg );
			setCmdReg( cSetAtn );
			messageAckNeeded = false;
			break;

	    default:
			WRITE_REGISTER( rFFO, kSCSIMsgRejectMsg );
			setCmdReg( cSetAtn );
			messageAckNeeded = false;
			break;
	    }
	    break;

	default: 						/* all others are unacceptable.	*/
		WRITE_REGISTER( rFFO, kSCSIMsgRejectMsg );
		setCmdReg( cSetAtn );
	    messageAckNeeded = false;
	}/* end SWITCH on Message byte */

	if ( messageAckNeeded )
	    setCmdReg( cMsgAcep );

	return ioReturn;
}/* end fsmProcessMessage */


	/* Just completed the SCMD_TRANSFER_INFO operation for command.	*/
void CurioSCSIController::fsmSendingCmd()
{
	fBusState = SCS_INITIATOR;
	return;
}/* end fsmSendingCmd */


	/* Follow SCSI Phase change. Called while SCS_INITIATOR. */

void CurioSCSIController::fsmPhaseChange()
{
	int		    i;


	ELG( 0, fSaveStatus, 'fPhC', "fsmPhaseChange" );

	fCurrentBusPhase = fSaveStatus & mPhase;

	switch ( fCurrentBusPhase )
	{
	case kBusPhaseCMD:
	    	/* The normal case here is after a host-initiated SDTR sequence.	*/
	    setCmdReg( cFlshFFO );
	    putCDBIntoFIFO();
	    setCmdReg( cIOXfer );			/* Start non-DMA xfer	*/
	    fMsgInState = kMsgInInit;
	    fBusState = SCS_SENDINGCMD;
	    break;

	case kBusPhaseDATI:				/* From target to Initiator (read)	*/
	case kBusPhaseDATO:				/* To Target from Initiator (write) */
	    fMsgInState = kMsgInInit;
	    fsmStartDataPhase( fCurrentBusPhase == kBusPhaseDATI );
	    fCkForAnotherInt = false;
	    break;

	case kBusPhaseSTS:			/* Status from Target to Initiator	*/
	    	/* fsmCompleting will collect the STATUS byte (and		*/
			/* hopefully a MSG) from the FIFO when this completes.	*/
	    fMsgInState = kMsgInInit;
	    fBusState = SCS_COMPLETING;
	    setCmdReg( cFlshFFO );
	    setCmdReg( cCmdComp );
	    break;

	case kBusPhaseMSGI:		/* Message from Target to Initiator */
		if ( fBusState != SCS_RESELECTING )
	    	 fBusState  = SCS_GETTINGMSG;
	 	setCmdReg( cNOP );
		setCmdReg( cIOXfer );
	    break;

	case kBusPhaseMSGO:		/* Message from Initiator to Target */
	    fMsgInState = kMsgInInit;
	    setCmdReg( cFlshFFO );

	    if ( fpMsgPut == fpMsgOut )
			*fpMsgOut++ = kSCSIMsgNop;	/* no message waiting to be sent.	*/


	    for ( i = 0; i < 16 && fpMsgPut < fpMsgOut; i++ )
		{	WRITE_REGISTER( rFFO , *fpMsgPut );
			fpMsgPut++;
	    }

	    if ( fpMsgPut == fpMsgOut )
			 fpMsgPut =  fpMsgOut = fMsgOutBuffer;

	    fBusState = SCS_SENDINGMSG;
	    	/* ATN is automatically cleared when transfer info completes.	*/
	    setCmdReg( cIOXfer );			/* Start non-DMA xfer	*/
	    break;

	default:
	    fsmStartErrorRecovery( kIOReturnInternalError );
	    break;
	}
	return;
}/* end fsmPhaseChange */


void CurioSCSIController::fsmStartDataPhase( bool isDataInPhase )
{
	IOReturn	    ioReturn	= kIOReturnSuccess;


	ELG( fCmd, fThisTransferLength, 'fDat', "fsmStartDataPhase" );

		/* DataIn phase is legal if this is a read command:	*/

	if ( isDataInPhase != !fCmdData->isWrite )
	{
	    ioReturn = kIOReturnInternalError;
		PAUSE( fCmd, isDataInPhase, 'Dir-', "fsmStartDataPhase - data direction" );
	}

	if ( ioReturn == kIOReturnSuccess )
	{
	    ioReturn = generateCCL();
	}

	if ( ioReturn == kIOReturnSuccess )
	{
	    if ( fThisTransferLength == 0 )
		{
			ioReturn = kIOReturnInternalError;
			PAUSE( fCmd, 0, 'Dat0', "fsmStartDataPhase - no data" );
	    }
	}

	if ( ioReturn == kIOReturnSuccess )
	{
	//	IOSetDBDMACommandPtr(		fDBDMAAddr, fCCLPhysAddr );
		fDBDMAAddr->commandPtrLo = SWAP( fCCLPhysAddr );
		SynchronizeIO();

	//	IOSetDBDMAChannelControl(	fDBDMAAddr, kdbdmaRun * 0x1001 );
	//	fDBDMAAddr->channelControl = SWAP( kdbdmaRun * 0x1001 );
		fDBDMAAddr->channelControl = SWAP( 0x80008000 );	// set RUN bit
		SynchronizeIO();

		WRITE_REGISTER( rXCM, fThisTransferLength >> 8   );
		WRITE_REGISTER( rXCL, fThisTransferLength & 0xFF );
		setCmdReg( cDMAXfer );

	    fBusState = SCS_DMACOMPLETE;
	}

	if ( ioReturn != kIOReturnSuccess )
	{
		PAUSE( 0, 0, 'Dat-', "fsmStartDataPhase - data phase error" );
		fsmStartErrorRecovery( ioReturn );
	}
	return;
}/* end fsmStartDataPhase */


	/* Start error recovery by doing something reasonable for the				*/
	/* current bus phase. This is called when we enter error recovery			*/
	/* (SCS_DEATH_MARCH) from an interrupt or other bus phase action handler.	*/

void CurioSCSIController::fsmStartErrorRecovery( IOReturn status )
{

	ELG( fCmd, 0, 'fSER', "fsmStartErrorRecovery" );
	killActiveCommand( status );
		/* Clean out the SCSI and DBDMA hardware	*/
	setCmdReg( cFlshFFO );
	setCmdReg( cNOP | bDMAEnBit );	// clear TC zero bit
//	IODBDMAReset( fDBDMAAddr );
	fDBDMAAddr->channelControl = SWAP( 0x20002000 );		// set FLUSH bit
		SynchronizeIO();
	fDBDMAAddr->channelControl = SWAP( 0x80000000 );		// clr RUN   bit
		SynchronizeIO();

	if ( fBusBusy == false )
		 fBusState = SCS_DISCONNECTED;
	else fBusState = SCS_DEATH_MARCH;/* We will continue at fsmContinueErrorRecovery	*/

	fCkForAnotherInt = false;
	return;
}/* end fsmStartErrorRecovery */


	/* Manage error recovery by responding to a target interrupt while in		*/
	/* an error recovery state. On exit, the bus state will be as follows:		*/
	/*	SCS_DEATH_MARCH	Still in error recovery. Call fsmContinueErrorRecovery	*/
	/*			to continue processing target requests.							*/
	/*	SCS_DISCONNECTED	The target is finally off the bus. We can restart	*/
	/*			normal operation.												*/
	/* Each bus phase requires a different action:								*/
	/*	Data Out	Clean up after DMA.											*/
	/*	Data In	Clean up after DMA.												*/
	/*	Command	Flush the FIFO													*/
	/*	Status	Drain the FIFO, if we get two bytes, just ACK the message		*/
	/*	Message Out Do nothing													*/
	/*	Message In	Read the message byte, ACK it.								*/

void CurioSCSIController::fsmErrorRecoveryInterruptService()
{
	setCmdReg( cFlshFFO );

	switch ( fCurrentBusPhase )
	{
	case kBusPhaseDATO:
	case kBusPhaseDATI:
	//	IODBDMAReset( fDBDMAAddr );
		fDBDMAAddr->channelControl = SWAP( 0x20002000 );		// set FLUSH bit
			SynchronizeIO();
		fDBDMAAddr->channelControl = SWAP( 0x80000000 );		// clr RUN   bit
			SynchronizeIO();
	    setCmdReg( cNOP | bDMAEnBit );	// clear TC zero bit
	    break;

	case kBusPhaseCMD:
	    break;

	case kBusPhaseSTS:
	    if ( fSaveInterrupt & iFuncComp )
		{
			/* We got both bytes: ACK the command complete	*/
			setCmdReg( cMsgAcep );
	    }
	    break;

	case kBusPhaseMSGO:
	    break;

	case kBusPhaseMSGI:
	    setCmdReg( cMsgAcep );
	    break;

	case kBusPhaseBusFree:	/* Selecting */
	    break;

	default:
	    killActiveCommand( kIOReturnInternalError );
		resetBus();
	    break;
	}
	return;
}/* end fsmErrorRecoveryInterruptService */


	/* Continue to manage error recovery. We are here because the target	*/
	/* is still on the bus and doing strange bus phase things. Follow the	*/
	/* target bus phase (one phase at a time) until the target disconnects.	*/
	/* We just run bit-bucket commands until the target gives up.			*/
	/* (Note that this means that we might send a Command Complete message	*/
	/* to the target. We'll look for this case and send an Abort or			*/
	/* Abort Tag instead.)													*/

void CurioSCSIController::fsmContinueErrorRecovery()
{
	UInt8		msgByte;


	quickCheckForInterrupt();


	if ( fBusBusy )
	{
		interruptPending();
	    if ( fSaveInterrupt & iDisconnect )
			fBusBusy = false;
	}

	if ( fBusBusy == false )
	{
	    fBusState = SCS_DISCONNECTED;
	}
	else
	{
	    fCurrentBusPhase = fSaveStatus & mPhase;

	    switch ( fCurrentBusPhase )
		{
	    case kBusPhaseMSGO:
			msgByte = kSCSIMsgAbort;
			WRITE_REGISTER( rFFO, msgByte );
			setCmdReg( cIOXfer );			/* Start non-DMA xfer	*/
			break;

	    case kBusPhaseDATO:
	    case kBusPhaseCMD:
			WRITE_REGISTER( rXCM, 0xFF );
			WRITE_REGISTER( rXCL, 0xFF );
			setCmdReg( cDMAXferPad );	/* DMA out pad	*/
			break;

	    case kBusPhaseMSGI:
	    case kBusPhaseDATI:
	    case kBusPhaseSTS:
			WRITE_REGISTER( rXCM, 0xFF );
			WRITE_REGISTER( rXCL, 0xFF );
			setCmdReg( cXferPad );	/* non-DMA out pad	*/
			break;

	    default:
			killActiveCommand( kIOReturnInternalError );
			resetBus();
			break;
	    }
	}/* end SWITCH on current phase */

	return;
}/* end fsmContinueErrorRecovery */



IOReturn CurioSCSIController::getReselectionTargetID( UInt8 selectByte )
{
	register UInt8		targetBits = selectByte;
	register UInt8		targetID	= 0;
	register UInt8		bitValue;	/* Supress warning  */


	if ( (targetBits & fInitiatorIDMask) == 0 )
	{
	    PAUSE( 0, selectByte, 'IId-', "getReselectionTargetID -  Reselection failed: initiator ID bit not set" );
		return kIOReturnInternalError;
	}

	targetBits	= selectByte;
	targetBits &= ~fInitiatorIDMask;		/* Remove our bit	    */
	if ( targetBits == 0 )
	{
	    PAUSE( 0, selectByte, 'TId-', "getReselectionTargetID -  Reselection failed: Target ID bit not set" );
		return kIOReturnInternalError;
	}

	bitValue = targetBits;

	if ( bitValue > 0x0F ) { targetID += 4;	bitValue >>= 4; }
	if ( bitValue > 0x03 ) { targetID += 2; bitValue >>= 2; }
	if ( bitValue > 0x01 )	 targetID += 1;

	targetBits  &= ~(1 << targetID);	/* Remove the target mask	*/
	if ( targetBits != 0 )
	{
	    PAUSE( 0, selectByte, 'TTT-', "getReselectionTargetID -  Reselection failed: multiple targets" );
		return kIOReturnInternalError;
	}

	ELG( 0, targetID, '=Tar', "getReselectionTargetID" );

	fCurrentTargetLun.target = targetID;	/* Save the current target	*/
	return kIOReturnSuccess;
}/* end getReselectionTargetID */


void CurioSCSIController::reselectNexus()
{
	fCmd = findCommandWithNexus( fCurrentTargetLun, fTag );
	if ( fCmd )
	{	fCmdData = (PrivCmdData*)fCmd->getCommandData();
		ELG( fCmd, *(UInt16*)&fCurrentTargetLun<<16 | (UInt16)fTag, '=Nex', "reselectNexus" );
	}
	else
	{	if ( fTag != kInvalidTag )
			ELG( 0, *(UInt16*)&fCurrentTargetLun<<16 | (UInt16)fTag, 'Nex-', "reselectNexus" );
	}

    return;
}/* end reselectNexus */


    /* Send a command to the MESH chip. This may cause an interrupt.    */

void CurioSCSIController::setCmdReg( UInt8 command )
{
    ELG( 0, command, '=Cmd', "setCmdReg" );

    fCurioAddr[ rCMD ] = command;	/***** DO IT    *****/
    SynchronizeIO();

    return;
}/* end setCmdReg */



	/* This spin-loop looks for another chip operation - it prevents	*/
	/* exiting the interrupt service routine if the chip presents		*/
	/* another operation within ten microseconds. Note that this		*/
	/* must be called from an IOThread, not from a "real" primary		*/
	/* interrupt service routine.										*/
	/*	@return	true if an interrupt is pending.						*/

#define OVERTIME( now, end )					\
 	(   ((now)->tv_sec  > (end)->tv_sec)		\
	|| (((now)->tv_sec == (end)->tv_sec) && ((now)->tv_nsec > (end)->tv_nsec)) )


bool CurioSCSIController::quickCheckForInterrupt()
{
	mach_timespec_t		thisTime, endTime;


	IOGetTime( &endTime );					// calc the omega
	endTime.tv_nsec	+= 10000;				// 10 mikes from now
	if ( endTime.tv_nsec >= NSEC_PER_SEC )
	{
	    endTime.tv_sec	+= 1;
	    endTime.tv_nsec	-= NSEC_PER_SEC;
	}
		/* whiz til interrupt or time's up:	*/
	do
	{
	    if ( interruptPending() )
			return true;

	    IOGetTime( &thisTime );
	} while ( OVERTIME( &thisTime, &endTime ) == false );

	return false;
}/* end quickCheckForInterrupt */


	/* Determine if SCSI interrupt is pending. If so, store the current	*/
	/* chip state. Note that reading the interrupt register clears the	*/
	/* interrupt source, so this should be done once for each interrupt.*/
	/*	@return	true if an interrupt is pending.						*/

bool CurioSCSIController::interruptPending()
{
	bool	result = false;


		/* mlj - use WHILE to check for double interrupts	*/
		/* and get get most recent conditions.				*/

	while ( (fSaveStatus = fCurioAddr[ rSTA ]) & sIntPend )	// ??? volatile
	{
	    result			= true;
	    fSaveSeqStep	= fCurioAddr[ rSQS ];
	    fSaveInterrupt	= fCurioAddr[ rINT ];	/* this clears rSTA and rSQS	*/
		ELG( fSaveSeqStep, fSaveStatus<<16 | fSaveInterrupt, 'Regs', "interruptPending" );
	}
	return result;
}/* end interruptPending */



void CurioSCSIController::putCDBIntoFIFO()
{
	SCSICDBInfo		scsiCDB;
	UInt32			i;


	fCmd->getCDB( &scsiCDB );
	for ( i = 0; i < scsiCDB.cdbLength; i++ )
		WRITE_REGISTER( rFFO, scsiCDB.cdb[ i ] );

	return;
}/* end putCDBIntoFIFO */


	/* resetCurio - Reset the chip to hardware power on state.	*/

void CurioSCSIController::resetCurio()
{
	ELG( 0, 0, 'ResC', "resetCurio" );
	setCmdReg( cRstSChp );		/* Reset chip		*/
	IODelay( 50 );				/* Chip settle		*/
	setCmdReg( cNOP );			/* Re-enable chip	*/
	setCmdReg( cFlshFFO );		/* In a clean state	*/
	return;
}/* end resetCurio */


	/* resetHardware - Reusable hardware initializer function.	*/
	/* if busReset is true, this includes a SCSI bus reset.		*/

IOReturn CurioSCSIController::resetHardware( bool busReset )
{
	IOReturn		ioReturn = kIOReturnSuccess;
	UInt8			configValue;
	UInt8			clockConversionFactor;
	UInt8			defaultSelectionTimeout;
	UInt8			temp;


		/* Temp for debugging	*/

	fSCSIClockRate = kChipDefaultBusClockMHz;

	ELG( fSCSIClockRate, busReset, 'ResH', "resetHardware" );

		/* First of all, reset interrupts, the SCSI chip, and the DMA engine.	*/

//	resetCurio();							/* Clear out the chip	    */

	temp = fCurioAddr[ rINT ];				/* Clear pending interrupt  */

//	IODBDMAReset( fDBDMAAddr );				/* Stop the DMA engine	    */
	fDBDMAAddr->channelControl = SWAP( 0x20002000 );		// set FLUSH bit
		SynchronizeIO();
	fDBDMAAddr->channelControl = SWAP( 0x80000000 );		// clr RUN   bit
		SynchronizeIO();

		/* Initialize the chip:	*/

	WRITE_REGISTER( rCF2, CF2_EPL );		/* Enable Phase Latching	*/
	setCmdReg(cNOP | bDMAEnBit);			/* DMA Nop					*/

		/* Init state variables:	*/

	fCkForAnotherInt	= false;
	fBusState			= SCS_DISCONNECTED;
	fBusBusy			= false;
	fCurrentBusPhase	= kBusPhaseBusFree;

		/* Smash all active command state (just in case).	*/

	fCmd		= NULL;
	fMsgInState	= kMsgInInit;
	fpMsgOut	= fpMsgPut = fMsgOutBuffer;

    fCurrentTargetLun.target	= kInvalidTarget;
	fCurrentTargetLun.lun		= kInvalidLUN;

		/* Configuration Register 1									*/
		/* Disable interrupt on initiator-instantiated bus reset	*/
		/* (is this correct?)										*/
		/* Enable parity.											*/
		/* Set default bus ID (7) (This should be overriden by the	*/
		/* Inspector)												*/

	fInitiatorID = kDefaultInitiatorID;
	configValue = CF1_SRD | CF1_ENABPAR | fInitiatorID;

#define gOptionExtendTiming	0
	if ( gOptionExtendTiming )
	{		/* Per instance table. This slows down transfers on the bus.	*/
	    configValue |= CF1_SLOW;
	}

	WRITE_REGISTER( rCF1, configValue );

		/* Clock factor and select timeout.	*/

		/* Use the clock frequency (in MHz) to select the clock conversion		*/
		/* factor. According to the NCR53CF94/96 data manual, the conversion	*/
		/* factor is defined by the following table: Currently, we don't allow	*/
		/* the caller to change selection timeout from the ANSI standard		*/
		/* 250 Msec to avoid having to support floating-point register			*/
		/* manipulation															*/

	if ( fSCSIClockRate < 10)	fSCSIClockRate	= 10;
	if ( fSCSIClockRate > 40 )	fSCSIClockRate	= 40;

	if ( fSCSIClockRate <= 10 )
	{
	    clockConversionFactor	= ccf10MHz;
	    defaultSelectionTimeout	= SelTO16Mhz;
	}
	else if ( fSCSIClockRate <= 15 )
	{
	    clockConversionFactor	= ccf11to15MHz;
	    defaultSelectionTimeout	= SelTO16Mhz;
	}
	else if ( fSCSIClockRate <= 20 )
	{
	    clockConversionFactor	= ccf16to20MHz;
	    defaultSelectionTimeout	= SelTO16Mhz;
	}
	else if ( fSCSIClockRate <= 25 )
	{
	    clockConversionFactor	= ccf21to25MHz;
	    defaultSelectionTimeout	= SelTO25Mhz;
	}
	else if ( fSCSIClockRate <= 30 )
	{
	    clockConversionFactor	= ccf26to30MHz;
	    defaultSelectionTimeout	= SelTO33Mhz;
	}
	else if ( fSCSIClockRate <= 35 )
	{
	    clockConversionFactor	= ccf31to35MHz;
	    defaultSelectionTimeout	= SelTO40Mhz;
	}
	else
	{
	    clockConversionFactor	= ccf31to35MHz;
	    defaultSelectionTimeout	= SelTO40Mhz;
	}

	WRITE_REGISTER( rCKF, clockConversionFactor );

	WRITE_REGISTER( rINT, defaultSelectionTimeout);

		/* Configuration Register 2 - enable extended features	*/
		/* - mainly, 24-bit transfer count.						*/

	WRITE_REGISTER( rCF2, CF2_EPL );

		/* Configuration Register 3	*/

	configValue = 0;

#define gOptionFastModeEnable				0
#define gOptionFastModeSupportedByHardware	0

	if ( (gOptionFastModeEnable && gOptionFastModeSupportedByHardware)
	    || fSCSIClockRate > 25 )
	{
	    configValue |= CF3_FASTSCSI;
	}

	WRITE_REGISTER( rCF3, configValue );

		/* Configuration Register 4 - glitch eater, active negation.	*/
		/* Let's not worry about these whizzy features just yet.		*/

	WRITE_REGISTER( rCF4, 0 );

		/* Go to async xfer mode for now.	*/

	WRITE_REGISTER( rFOS, 0 );

		/* Reset SCSI bus, wait, clear possible interrupt.	*/

	if ( busReset )
	{
		setCmdReg( cRstSBus );
	    IOSleep( kSCSIResetDelay );
	    temp = fCurioAddr[ rINT ];		// clear the Interrupt register
	}
	return ioReturn;
}/* end resetHardware */


void CurioSCSIController::releaseHardwareMemoryMaps()
{
	if ( fDBDMAMemoryMap )
	{
	    fDBDMAMemoryMap->release();
	    fDBDMAMemoryMap = NULL;
	}

	if ( fSCSIMemoryMap )
	{
	    fSCSIMemoryMap->release();
	    fSCSIMemoryMap = NULL;
	}

	if ( fCCL )
	{
	    IOFree( fCCL, fCCLSize );
	}

	return;
}/* end releaseHardwareMemoryMaps */


	/* When a legitimate data phase starts, this method			*/
	/* is called to configure the DBDMA Channel Command list.	*/

IOReturn CurioSCSIController::generateCCL()
{
	IODBDMADescriptor	*dp; 					/* current descriptor		*/
	IODBDMADescriptor	*dpMax;					/* past the last descriptor	*/
	UInt32				dbdmaOp;				/* DBDMA opcode				*/
	UInt32				rangeByteCount, actualRanges;
	UInt32				i;
	IOPhysicalSegment		range[ kMaxMemoryCursorSegments ];


	
//	IODBDMAReset( fDBDMAAddr );		/* make sure that the DBDMA is idle.	*/
	fDBDMAAddr->channelControl = SWAP( 0x20002000 );		// set FLUSH bit
		SynchronizeIO();
	fDBDMAAddr->channelControl = SWAP( 0x80000000 );		// clr RUN   bit
		SynchronizeIO();

	if ( fCmdData->isWrite )		/* Select the correct DBDMA:			*/
		 dbdmaOp	= OUTPUT_MORE;
	else dbdmaOp	=  INPUT_MORE;

		/* How many descriptors can we store (need some slop for the	*/
		/* terminator commands).										*/
		/* Get a pointer to the first free descriptor and the total		*/
		/* number of bytes left to transfer in this I/O request.		*/

	dp		= (IODBDMADescriptor*)fCCL;
	dpMax	= (IODBDMADescriptor*)(fCCL + fCCLSize - 256);

		/* fThisTransferLength will contain the actual number	*/
		/* of bytes we intend to transfer in this DMA request,	*/
		/* which is needed when DMA completes to recover the	*/
		/* residual transfer length.							*/
	fThisTransferLength	= 0;

	while ( (dp < dpMax) && (fThisTransferLength < kMaxDMATransfer) )
	{
		actualRanges = fMemoryCursor->getPhysicalSegments(	fCmdData->mdp,
								fCmdData->results.bytesTransferred + fThisTransferLength,
								range,
								(unsigned int)kMaxMemoryCursorSegments,
								0, &rangeByteCount );
		if ( actualRanges == 0 )
			break;

		for ( i = 0; i < actualRanges; i++ )
		{
			ELG( range[ i ].location, range[ i ].length, '=Rng', "generateCCL - range" );
			dp->operation	= SWAP( dbdmaOp | range[ i ].length );
			dp->address		= SWAP(   (UInt32)range[ i ].location );
			dp->cmdDep		= 0;		// no branch address
			dp->result		= 0;
			dp++;
		}/* end FOR each range */

		fThisTransferLength += rangeByteCount;
	}/* end WHILE there is data to xfer */

	if ( (UInt8*)dp > fCCL )
	{
			/* We stored at least one descriptor.							*/
			/* Change the last one so it is an INPUT_LAST or OUTPUT_LAST.	*/

		dp->operation |= SWAP( IO_LAST );
	}

	dp->operation	= SWAP( STOP_CMD );
	dp->address		= 0;
	dp->cmdDep		= 0;
	dp->result		= 0;
	dp++;

	if( dp >= dpMax )
		PAUSE( dpMax, dp, 'Big-', "InitCmdCCL - CCL overrun" );

	flush_dcache( (vm_offset_t)fCCL,	(UInt32)dp - (UInt32)fCCL,	false );

	return kIOReturnSuccess;
}/* end generateCCL */


void CurioSCSIController::killActiveCommand( IOReturn finalStatus )
{
	if ( fCmd )
	{
		fCmdData->results.returnCode = finalStatus;
		completeCommand();
	}
	return;
}/* end killActiveCommand */


void CurioSCSIController::resetBus()
{
	setCmdReg( cRstSBus );
	super::resetOccurred();
	IOSleep( kSCSIResetDelay );
	enableCommands();
	return;
}/* end resetBus */