Source to bsd/dev/ppc/drvApple96_SCSI/Apple96BusState.m


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

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
 * Reserved.  This file contains Original Code and/or Modifications of
 * Original Code as defined in and that are subject to the Apple Public
 * Source License Version 1.0 (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.
 * 
 * The 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 Apple Computer Inc. All Rights Reserved.
 * @revision	1997.02.17	Initial conversion from AMDPCSCSIDriver sources.
 *
 * Set tabs every 4 characters.
 *
 * Apple96Chip.m - Chip-specific methods for Apple96 SCSI driver.
 *
 * Edit History
 * 1997.02.18	MM		Initial conversion from AMDPCSCSIDriver sources. 
 * 1997.04.17	MM		Removed SCS_PHASECHANGE (not needed)
 * 1997.06.24	MM		Radar 1669736 - correctly store autosense status
 * 1997.09.10	MM		Radar 1678545 - Don't clear the gBusBusy flag until we
 *				get a "disconnected" interrupt.
 */
#import "Apple96SCSI.h"
#import "Apple96BusState.h"
#import "Apple96SCSIPrivate.h"
#import "Apple96CurioPublic.h"
#import "Apple96CurioPrivate.h"
#import "Apple96CurioDBDMA.h"
#import "Apple96HWPrivate.h"
#import "Apple96ISR.h"
#import "MacSCSICommand.h"
#import "bringup.h"
#import <driverkit/generalFuncs.h>
#import <kernserv/prototypes.h>

#define IO_R_BV	0xDEADBEEF	/* TEMP */

extern IONamedValue scsiMsgValues[];	/* In Apple96ISR.m */
	
@implementation Apple96_SCSI(BusState)
/*
 * Disconnected - only legal event here is reselection.
 * [self fsmHandleReselectionInterrupt] may set gBusState to SCS_INITIATOR
 */
- (void) fsmDisconnected
{
		UInt8				selectByte;
		UInt8				target;
		
		ENTRY("Bdi fsmDisconnected");
		ddmChip("fsmDisconnected\n", 1,2,3,4,5);
		if ((gSaveInterrupt & iReselected) != 0) {
			[self fsmHandleReselectionInterrupt];
		}
		else if ((gSaveInterrupt & (iSelected | iSelectWAtn)) != 0) {
			/*
			 * To do: allow selection by an external target and support
			 * a few mandatory commands:
			 *	(PowerUp:		gPendingTargetError = Unit Attention)
			 *	Inquiry			We're a CPU
			 *	Test Unit Ready:
			 *					if (gPendingTargetError == Unit Attention)
			 *						Check Condition
			 *					else {
			 *						Success
			 *					}
			 *	All others		gPendingTargetError := Illegal Command.
			 *					Check Condition
			 *	Request Sense:	Send current error, clear current error.
			 */
	    SynchronizeIO();
	    selectByte = CURIOgetFifoByte();
			selectByte &= ~gInitiatorIDMask;
			for (target = 0; target < 8; target++) {
				if ((selectByte & (1 << target)) != 0) {
					break;
				}
			}
			IOLog("%s: Selected by external target %d -- not supported\n",
				[self name],
				target
			);
	    CURIOdisconnect();
		}
		else if (gFlagBusBusy == FALSE) {
			/*
			 * disconnect interrupted while disconnected
			 * This always happens when we finish an I/O request.
			 * gFlagBusBusy is cleared when the "bus disconnected" bit
			 * is set in the NCR 53C96 interrupt register.
			 */
		}
		else {
			/*
			 * Since we're disconnected, just ignore the interrupt
			 */
			if (gActiveCommand != NULL
				&& gActiveCommand->scsiReq != NULL
				&& gActiveCommand->scsiReq->driverStatus == SR_IOST_INVALID) {
#if 0 /* Radar 1678545 */
				IOLog("fsmDisconnected: timeout, target %d, int %02x, status %02x, step %02x\n",
					gActiveCommand->scsiReq->target,
					gSaveInterrupt & 0xFF,
					gSaveStatus & 0xFF,
					gSaveSeqStep & 0xFF
				);
#endif 
				gActiveCommand->scsiReq->driverStatus = SR_IOST_SELTO;
			}
			if (gActiveCommand != NULL) {
		[self ioComplete
			    : gActiveCommand
		    finalStatus : SR_IOST_INVALID
		];
			}
			gCurrentBusPhase = kBusPhaseBusFree;
		}
		EXIT();
}

/*
 * 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) fsmWaitForBusFree
{
		if ((gSaveInterrupt & (iReselected | iSelected | iSelectWAtn)) != 0) {
			/*
			 * Hmm, 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.)
			 */
			[self fsmDisconnected];
		}
		else if (gFlagBusBusy == FALSE) {
			/*
			 * This is expected: the bus has just gone free after a command
			 * completed. We can now safely try to start another command.
			 */
			gBusState = SCS_DISCONNECTED;
		}
		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.
			 */
			[self fsmStartErrorRecovery
							: SR_IOST_HW
					reason	: "Unexpected interrupt when disconnecting"
			];
		}
}
	
/*
 * This is called from fsmDisconnected when we receive a reselected interrupt.
 */
- (void) fsmHandleReselectionInterrupt
{
		UInt8					selectByte;
		UInt32	 				fifoDepth;
		UInt8					msgByte;
		IOReturn				ioReturn;
		const char				*reason = NULL;

		ENTRY("Bre fsmHandleReselectionInterrupt");
		/* 
		 * Make sure there's a selection byte and an identify message in the fifo.
		 */
		ioReturn = IO_R_SUCCESS;
		gFlagBusBusy = ((gSaveInterrupt & iDisconnect) == 0);	/* Radar 1678545 */
	SynchronizeIO();
	fifoDepth = CURIOgetFifoCount();
		ddmChip("reselect: fifoDepth %d\n", fifoDepth, 2,3,4,5);
		if (fifoDepth != 2) {
			ddmError("reselection, fifoDepth %d\n", fifoDepth, 
				2,3,4,5);
#if LOG_ERROR_RECOVERY
			IOLog("%s: Bad FIFO count (%d) (expect 2) on Reselect\n",
				[self name],
				fifoDepth
			);
#endif /* LOG_ERROR_RECOVERY */
			ioReturn = SR_IOST_HW;
			reason =  "Bad FIFO count on reselect";
		}
		if (ioReturn == IO_R_SUCCESS) {
	    SynchronizeIO();
	    selectByte = CURIOgetFifoByte();
			ddmChip("reselection: select byte %02x\n", selectByte, 2, 3, 4, 5);
			ioReturn = [self getReselectionTargetID : selectByte];
			if (ioReturn != IO_R_SUCCESS) {
				reason = "Bad selection value on reselect";
			}
		}
		if (ioReturn == IO_R_SUCCESS) {
			/*
			 * gCurrentTarget has the bus ID of the target that is reselecting.
			 * The first message byte must be an Identify with the LUN.
			 */
	    SynchronizeIO();
	    msgByte = CURIOgetFifoByte();
			if ((gSaveStatus & sParityErr) != 0) {
				ioReturn = SR_IOST_PARITY;
				reason = "Parity error on reselect";
			}
		}
		if (ioReturn == IO_R_SUCCESS) {
			if ((msgByte & kScsiMsgIdentify) == 0) {
				ioReturn = SR_IOST_BV;
				reason = "Bad ID Message (no identify) on reselect";
			}
			gCurrentLUN = msgByte & ~kScsiMsgIdentify;
	    /* gFlagBusBusy = TRUE; -- Radar 1678545: move to method start  */
			gBusState = 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 (that's the
			 * job of fsmReselectionAction).
			 *
			 * In case of sync mode, we need to load target context right
			 * now, before dropping ACK, because the target might go
			 * straight to a data in or data out as soon as ACK drops.
			 */
	    CURIOmessageAccept();   /* Accept the identify message  */
			ddmChip("Successful reselect from %d.%d\n",
					gCurrentTarget,
					gCurrentLUN,
					3, 4, 5
				);
		}
		if (ioReturn != IO_R_SUCCESS) {
			[self fsmStartErrorRecovery
							: ioReturn
					reason	: reason
			];
		}
		EXIT();
}

/*
 * 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) fsmSelecting
{
		IOReturn				ioReturn;
		UInt8					fifoDepth;
		CommandBuffer			*cmdBuf;
		IOSCSIRequest			*scsiReq = NULL;
		const char				*reason = NULL;

		ENTRY("Bse fsmSelecting");
		ddmChip("fsmSelecting\n", 1,2,3,4,5);
		ASSERT(gActiveCommand != NULL && gActiveCommand->scsiReq != NULL);
		scsiReq = gActiveCommand->scsiReq;
		ioReturn = IO_R_SUCCESS;
		gFlagBusBusy = ((gSaveInterrupt & iDisconnect) == 0);
		if (gFlagBusBusy == FALSE) {
			/*
			 * selection timed-out. Abort this request.
			 */
			ddmChip("***SELECTION TIMEOUT for target %d.%d\n",
				scsiReq->target,
				scsiReq->lun,
				3,4,5
			);
	    CURIOflushFifo();
			gBusState = SCS_DISCONNECTED;
#if 0 /* Radar 1678545 */
			IOLog("fsmSelecting: timeout, target %d, int %02x, status %02x, step %02x\n",
				gActiveCommand->scsiReq->target,
				gSaveInterrupt & 0xFF,
				gSaveStatus & 0xFF,
				gSaveSeqStep & 0xFF
			);
#endif 
	    [self ioComplete
			: gActiveCommand
		finalStatus : SR_IOST_SELTO
	    ];
			gActiveCommand = NULL;
		}
		else if (gSaveInterrupt == (iFuncComp | iBusService)) {
			ddmChip("selection seqstep=%d\n", 
				gSaveSeqStep & INS_STATE_MASK, 2,3,4,5);
			switch (gSaveSeqStep & INS_STATE_MASK) {
	    case 0: 
				/*
				 * No message phase. If we really wanted one,
				 * this could be significant...
				 */
				if (gActiveCommand->queueTag != QUEUE_TAG_NONTAGGED) {
					/*
					 * This target can't do command queueing.
					 */
					[self disableMode : kTargetModeCmdQueue];
					/*
					 * Clear the queue tag so we don't get here on
					 * an eventual autosense.
					 */
					gActiveCommand->queueTag = QUEUE_TAG_NONTAGGED;
				}
				/*
				 * OK, let's try to continue following phase changes.
				 */
				gBusState = 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
				 * msg out phase). We want to send 5 bytes. 
				 * Drop them into currMsgOut[] and prime the  
				 * msgOutState machine.
				 */
				gBusState = SCS_INITIATOR;
				break;
	    case 2: 
				/*
				 * Either no command phase, or incomplete message
				 * transfer.
				 */
		SynchronizeIO();
		fifoDepth = CURIOgetFifoCount();
#if LOG_ERROR_RECOVERY
				IOLog("%s: Incomplete select (step 2); fifoDepth %d phase %s\n",
					[self name],
					fifoDepth, 
		    IOFindNameForValue(gSaveStatus & mPhase, scsiPhaseValues)
				);
#endif /* LOG_ERROR_RECOVERY */
				if (gActiveCommand->queueTag != QUEUE_TAG_NONTAGGED) {
					/*
					 * This target can't do command queueing.
					 */
					[self disableMode : kTargetModeCmdQueue];
				}
				/*
				 * Spec says ATN is asserted if all message bytes
				 * were not sent.
				 */
				if (fifoDepth > gActiveCommand->cdbLength) {
		    CURIOclearATN();
				}
				/*
				 * 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.
				 */
		CURIOflushFifo();   /* The command is still in the fifo */
				/*
				 * OK, let's try to continue following phase changes.
				 */
				gBusState = SCS_INITIATOR;
				break;
			default:
				/* gFlagBusBusy = TRUE;		-- Radar 1678545 */
				ioReturn = SR_IOST_HW;
				reason = "Selection sequence Error (strange state)";
				break;
			}
		}
		else if ((gSaveInterrupt & iReselected) != 0) {
			/*
			 * 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.	 
			 */
			cmdBuf = gActiveCommand;
		//	[ self deactivateCmd ];		// mlj - done by pushbackCurrentRequest
			[self pushbackCurrentRequest : cmdBuf];
			ddmChip("reselect while trying to select target %d.%d\n",
					scsiReq->target,
					scsiReq->lun,
					3,4,5
				);
			gBusState = SCS_DISCONNECTED;
			/*
			 * Go deal with reselect.
			 */
			[self fsmDisconnected];
		}
		else if (gSaveInterrupt == iFuncComp
			  && (gSaveSeqStep & INS_STATE_MASK) == 2
			  && (gSaveStatus & mPhase) == kBusPhaseMSGI
			  && (gLastMsgOut[0] & kScsiMsgIdentify) == kScsiMsgIdentify
			  && (gLastMsgOut[0] & 0x07) != 0) {
				/*
				 * This shouldn't happen according to the NCR documentation,
				 * but we saw it if the bus scanner executes INQUIRY on
				 * a non-zero LUN on an Apple CD-300. The CD is sending a
				 * Message Reject to our Identify message byte. We'll
				 * handle this in the Message In code.
				 */
				gBusState = SCS_INITIATOR;
		}
		else {
			ioReturn = SR_IOST_HW;
			reason = "Bogus select/reselect interrupt";
		}
		if (ioReturn != IO_R_SUCCESS) {
			[self fsmStartErrorRecovery
							: ioReturn
					reason	: "Error selecting target"
			];
		}
		EXIT();
}

/*
 * 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) fsmInitiator
{
		ENTRY("Bin fsmInitiator");
		ddmChip("fsmInitiator\n", 1,2,3,4,5);
		gBusState = SCS_INITIATOR;
		EXIT();
}

/*
 * We just did a SCMD_INIT_CMD_CMPLT command, hopefully all that's left is
 * to drop ACK. Command Complete message is handled in fscAcceptingMsg.
 * 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) fsmCompleting
{
		unsigned			fifoDepth;
		IOReturn			ioReturn;
	UInt8		    statusByte;
		const char			*reason = NULL;
		
		ENTRY("Bco fsmCompleting");
		ddmChip("fsmCompleting\n", 1,2,3,4,5);
		ASSERT(gActiveCommand != NULL && gActiveCommand->scsiReq != NULL);
		ioReturn = IO_R_SUCCESS;
		if ((gSaveInterrupt & iDisconnect) != 0) {
			ddmError("unexpected disconnect\n", 1,2,3,4,5);
			ioReturn = SR_IOST_HW;
			reason = "I/O complete, bus free before target completion complete";
		}
		if (ioReturn == IO_R_SUCCESS) {
	    SynchronizeIO();
	    fifoDepth = CURIOgetFifoCount();
			if ((gSaveInterrupt & iFuncComp) != 0) {
				/*
				 * Got both status and msg in fifo; ACK is still asserted.
				 */
				if (fifoDepth != 2) {
					/*
					 * This is pretty bogus - we expect a status and 
					 * msg in the fifo.
				   	 */
				   	ioReturn = SR_IOST_HW;
				   	reason = "I/O complete, incorrect fifo count (expecting two bytes)";
				}
				if (ioReturn == IO_R_SUCCESS) {
		    SynchronizeIO();
		    statusByte = CURIOgetFifoByte();
		    if (gActiveCommand->flagIsAutosense) {		/* Radar 1669736    */
			gActiveCommand->autosenseStatus = statusByte;
					}
					else {
			gActiveCommand->scsiReq->scsiStatus = statusByte;
					}
		    SynchronizeIO();
		    gMsgInBuffer[0] = CURIOgetFifoByte();
					gMsgInIndex		= 1;
					ioReturn		= [self fsmProcessMessage];
					reason			= "Message in failed";
				}
			}
			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.
				 */
				ddmError("fsmCompleting: status only on complete\n", 
					1,2,3,4,5);
				if (fifoDepth != 1) {
					ioReturn = SR_IOST_HW;
					reason = "I/O complete: incorrect fifo count (expecting one byte)";
				}
				if (gActiveCommand->flagIsAutosense) {				/* Radar 1669736	*/
		    SynchronizeIO();
		    gActiveCommand->autosenseStatus = CURIOgetFifoByte();
				}
				else {
		    SynchronizeIO();
		    gActiveCommand->scsiReq->scsiStatus = CURIOgetFifoByte();
				}
				/*
				 * Back to watching phase changes. Presumably, the target
				 * will switch to MSGI phase and complete the command
				 * at its leasure.
				 */
				gBusState = SCS_INITIATOR;
			}
		}
		if (ioReturn != IO_R_SUCCESS) {
			[self fsmStartErrorRecovery
							: ioReturn
					reason	: reason
			];
		}
		EXIT();
}

/*
 * DMA Complete.
 */
- (void) fsmDMAComplete
{
		unsigned		bytesMoved;

		ENTRY("Bdc fsmDMAComplete");
		ddmChip("fsmDMAComplete\n", 1,2,3,4,5);
		ASSERT(gActiveCommand != NULL && gActiveCommand->scsiReq != NULL);
		/*
		 * Stop the dma engine and retrieve the total number of
		 * bytes transferred. This will be
		 *	gActiveCommand->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.
		 */
		bytesMoved = [self curioDMAComplete];
		if ((gSaveStatus & sParityErr) != 0) {
			[self fsmStartErrorRecovery
							: SR_IOST_PARITY
					reason	: "SCSI Data Parity Error"
			];
		}
		else {
			/*
			 * Continue following phase changes.
			 */
			gBusState = SCS_INITIATOR;
		}
		EXIT();
}

/*
 * 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) fsmGettingMsg
{
		IOReturn			ioReturn;
		const char			*reason		= NULL;
		UInt32				fifoCount;
		UInt8				msgInByte;
		
		ENTRY("Bmi fsmGettingMsg");
		ioReturn = IO_R_SUCCESS;
		if (gFlagBusBusy == FALSE) {
			ddmChip("fsmGettingMsg: message In Disconnect\n", 1,2,3,4,5);
			/*
			 * This (non-fatal) error is handled on return...
			 */
			ioReturn = IO_R_INTERNAL;	/* Any non-zero non-fatal error status	*/
		}
		else {
	    SynchronizeIO();
	    fifoCount = CURIOgetFifoCount();
			if (fifoCount != 1) {
				ddmChip("Message in fifo count error, count = %d\n",
					fifoCount,
					2, 3, 4, 5
				);
				ioReturn = SR_IOST_HW;
				reason = "Message in fifo count error";
			}
		}
		if (ioReturn == IO_R_SUCCESS) {
	    SynchronizeIO();
	    msgInByte = CURIOgetFifoByte();
			if ((gSaveStatus & sParityErr) != 0) {
				ioReturn = SR_IOST_PARITY;
				reason = "Parity error getting Command Complete message";
			}
		}
		if (ioReturn == IO_R_SUCCESS) {
			if (gMsgInState == kMsgInIdle) {
				gMsgInCount = 0;
				gMsgInIndex = 0;
			}
			if (gMsgInIndex >= kMessageInBufferLength) {
				ioReturn = SR_IOST_HW;
				reason = "Message in too many bytes";
			}
		}
		if (ioReturn == IO_R_SUCCESS) {
			gMsgInBuffer[gMsgInIndex++] = msgInByte;
			switch (gMsgInState) {
			case kMsgInIdle:
				/*
				 * This is the first message byte. Check for 1-byte codes.
				 */
				ddmChip("fsmGettingMsg: msgByte = 0x%x (%s)\n",
					msgInByte,
					IOFindNameForValue(msgInByte, scsiMsgValues),
					3, 4, 5
				);
				if ((/* msgInByte >= kScsiMsgOneByteMin && */ msgInByte <= kScsiMsgOneByteMax)
				 || msgInByte >= kScsiMsgIdentify) {
					gMsgInState = kMsgInReady;
				}
				else if (msgInByte >= kScsiMsgTwoByteMin
					  && msgInByte <= kScsiMsgTwoByteMax) {
					/*
					 * This is a two-byte message. Set the count and
					 * read the next byte.
					 */
		    gMsgInState = kMsgInReading;	/* Need one more    */
					gMsgInCount = 1;
				}
				else {
					/*
					 * This is an extended message. The next byte has the count.
					 */
					gMsgInState = kMsgInCounting;
				}
				break;
			case kMsgInCounting:
				/*
				 * Read the count byte of an extended message.
				 */
				gMsgInCount = msgInByte;
				gMsgInState = kMsgInReading;
				break;
			case kMsgInReading:
				if (--gMsgInCount <= 0) {
					gMsgInState = kMsgInReady;
				}
				break;
			default:
				ASSERT(gMsgInState != 0xDEADBEEF /* Bogus state */);
				ioReturn = SR_IOST_BV;
				reason = "Message in bogus state";
			} /* Switch on message state */
		}
		if (ioReturn == IO_R_SUCCESS) {
			switch (gMsgInState) {
			case kMsgInReading:
			case kMsgInCounting:
				/*
				 * We have more message bytes to read. Accept this byte
				 * (this sets ACK) and setup to transfer the next byte.
				 */
		CURIOmessageAccept();
				/*
				 * Since the message accept command sets "interrupt", eat
				 * it here so we don't get a second interrupt.
				 */
		CURIOquickCheckForChipInterrupt();
		CURIOstartMSGIAction();
				gBusState = 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:
				gMsgInState = kMsgInIdle;
				ioReturn = [self fsmProcessMessage];
				if (ioReturn != IO_R_SUCCESS) {
					reason = "Can't process received message";
				}
				break;
			case kMsgInIdle:
			default:
				/*
				 * Hmm, that's strange: we should never be in idle state
				 * *after* successfully reading a message byte.
				 */
				ioReturn = SR_IOST_BV;
				reason = "Getting message: bogus idle state";
				break;
			}
		}
		if (ioReturn != IO_R_SUCCESS) {
			[self fsmStartErrorRecovery
							: ioReturn
					reason	: reason
			];
		}
		EXIT();
}

/*
 * Just finished a message in; ACK is false and the message has been read
 * into gMsgInBuffer[0..gMsgInIndex]. We have not ACK'ed the last byte yet.
 * If we fail here, the caller will start error recovery.
 */
- (IOReturn) fsmProcessMessage
{
		IOReturn			ioReturn			= IO_R_SUCCESS;
		const char			*reason				= "(Unknown)";
		UInt8				queueTag			= QUEUE_TAG_NONTAGGED;
		Boolean				messageAckNeeded	= TRUE;

		ENTRY("Bmp fsmProcessMessage");
		RAW_TAG(OSTag('*', "msg"), gMsgInBuffer[0]);
		/*
		 * Message in complete. Handle message(s) in currMsgIn[].
		 */
		if (gBusState == SCS_RESELECTING) {
			/*
			 * The only interesting message here is queue tag.
			 * (Hmm, what about target SDTR or Abort?)
			 */
			ASSERT(gActiveCommand == NULL);
			ASSERT(gCurrentTarget != kInvalidTarget && gCurrentLUN != kInvalidLUN);
			ASSERT(gMsgInIndex > 0);
			switch (gMsgInBuffer[0]) {
			case kScsiMsgHeadOfQueueTag:
			case kScsiMsgOrderedQueueTag:
			case kScsiMsgSimpleQueueTag:
				if (gMsgInIndex != 2) {
					ioReturn = SR_IOST_HW;
					reason = "Queue tag message: queue tag without tag value";
				}
				else {
					ioReturn = [self reselectNexusWithTag : gMsgInBuffer[1]];
					if (ioReturn != IO_R_SUCCESS) {
						reason = "Tagged queue reselection failed to locate command";
					}
				}
				break;
			default:
				ddmError("Strange message byte %02x from %d.%d while reselecting (expect tag)\n",
					gMsgInBuffer[0],
					gCurrentTarget,
					gCurrentLUN,
					4, 5
				);
				ioReturn = SR_IOST_HW;
				reason = "Reselection failed (expecting queue tag message)";
				break;				
			}
			if (ioReturn == IO_R_SUCCESS) {
				ASSERT(gActiveCommand != NULL);
				gActiveCommand->currentDataIndex = gActiveCommand->savedDataIndex;
		if (gActiveCommand->mem != NULL) {
		    [gActiveCommand->mem setState : &gActiveCommand->savedDataState];
		}
			}
			/*
			 * Since we process commands one by one, whack this command
			 * so we fall through the regular message handler.
			 */
			gMsgInBuffer[0] = kScsiMsgNop;
			gMsgInIndex = 1;
		}
		gBusState					= SCS_INITIATOR;
		gFlagNeedAnotherInterrupt	= TRUE;

		switch (gMsgInBuffer[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. 
			 */
			gBusState = SCS_WAIT_FOR_BUS_FREE;
			/* gFlagBusBusy = FALSE;	-- Radar 1678545: wait for interrupt */
			if (gActiveCommand != NULL) {
		[self ioComplete
			    : gActiveCommand
		    finalStatus : SR_IOST_GOOD
		];
			}
			ASSERT(gActiveCommand == NULL);
			break;

		case kScsiMsgDisconnect:
			ASSERT(gActiveCommand != NULL && gActiveCommand->scsiReq != NULL);
			if (gActiveCommand->scsiReq->disconnect == FALSE) {
				/*
				 * This is bogus; we could do a message reject, but
				 * ignoring it is simpler and, probably better.
				 */
				ddmChip("Illegal disconnect for %d.%d\n",
					gActiveCommand->scsiReq->target,
					gActiveCommand->scsiReq->lun,
					3, 4, 5
				);
#if LOG_ERROR_RECOVERY
				IOLog("%s: Unexpected disconnect attempt from target %d.%d\n",
						[self name],
						gActiveCommand->scsiReq->target,
						gActiveCommand->scsiReq->lun
				);
#endif /* LOG_ERROR_RECOVERY */
			}
			else {
				/*
				 * Some targets fail to do a restore pointers before
				 * disconnect if all requested data has been transferred.
				 * Do an implied save pointers if this is the case.
				 */
				if ( gActiveCommand->currentDataIndex >= gActiveCommand->scsiReq->maxTransfer )
					 gActiveCommand->savedDataIndex = gActiveCommand->currentDataIndex;

				if ( gActiveCommand->mem != NULL )
					   [ gActiveCommand->mem state : &gActiveCommand->savedDataState ];
				[ self disconnect ];
			}
			/*
			 * Exit the interrupt service routine so we don't miss a reselection interrupt.
			 */
			/* gFlagBusBusy = FALSE;	-- Radar 1678545: wait for interrupt */
			gBusState = SCS_WAIT_FOR_BUS_FREE;
			gFlagCheckForAnotherInterrupt = FALSE;
			break;
		case kScsiMsgSaveDataPointers:
			ASSERT(gActiveCommand != NULL && gActiveCommand->scsiReq != NULL);
			gActiveCommand->savedDataIndex = gActiveCommand->currentDataIndex;
	    if (gActiveCommand->mem != NULL) {
		[gActiveCommand->mem state : &gActiveCommand->savedDataState];
	    }
			break;
		case kScsiMsgRestorePointers:
			ASSERT(gActiveCommand != NULL && gActiveCommand->scsiReq != NULL);
			gActiveCommand->currentDataIndex = gActiveCommand->savedDataIndex;
	    if (gActiveCommand->mem != NULL) {
		[gActiveCommand->mem setState : &gActiveCommand->savedDataState];
	    }
			break;
		case kScsiMsgRejectMsg:
			/*
			 * Hmm, the only message reject we should see would have to be
			 * a queue tag (to a device that doesn't do queuing).
			 */
			switch (gLastMsgOut[0]) {
			case kScsiMsgHeadOfQueueTag:
			case kScsiMsgOrderedQueueTag:
			case kScsiMsgSimpleQueueTag:
				[self disableMode : kTargetModeCmdQueue];
				break;
			default:
				if (gLastMsgOut[0] >= kScsiMsgIdentify
				 && (gLastMsgOut[0] & 0x07) != 0) {
				 	ddmError("Target %d.%d rejected Identify message %02x\n",
				 		gCurrentTarget,
				 		gCurrentLUN,
				 		gLastMsgOut[0],
				 		4, 5
				 	);
#if LOG_ERROR_RECOVERY
					/*
					 * The target rejected an identify message for a non-zero
					 * LUN. We treat this as a selection failure and tell the
					 * target to get off the bus.
					 */
					IOLog("%s: Target %d.%d rejected Identify message %02x\n",
						[self name],
						gCurrentTarget,
						gCurrentLUN,
						gLastMsgOut[0]
					);
#endif /* LOG_ERROR_RECOVERY */
					/*
					 * When we send an Abort to a target, we expect that it
					 * will immediately go to bus free. Unfortunately, some
					 * targets go to MSGI and send us an Abort, too.
					 */
					if (gActiveCommand != NULL) {
						if (gActiveCommand->scsiReq != NULL
			 			 && gActiveCommand->scsiReq->driverStatus == SR_IOST_INVALID) {
#if 0
			IOLog("fsmProcessMsg abort: timeout, target %d, int %02x, status %02x, step %02x\n",
				gActiveCommand->scsiReq->target,
				gSaveInterrupt & 0xFF,
				gSaveStatus & 0xFF,
				gSaveSeqStep & 0xFF
			);
#endif 
							gActiveCommand->scsiReq->driverStatus = SR_IOST_SELTO;
						}
						/* [self ioComplete : gActiveCommand];	970610	*/
					}
				}
				else {
					ddmError("%s Message rejected from target %d.%d\n",
						IOFindNameForValue(gLastMsgOut[0], scsiMsgValues),
						gCurrentTarget,
						gCurrentLUN,
						4, 5
					);
#if LOG_ERROR_RECOVERY
					IOLog("%s: %s Message Rejected, that's strange.\n",
						[self name],
						IOFindNameForValue(gLastMsgOut[0], scsiMsgValues)
					);
#endif /* LOG_ERROR_RECOVERY */
					ioReturn = SR_IOST_HW;				/* 970610 */
				}
				/* ioReturn = SR_IOST_HW;	970610 */
				break;
			}
		case kScsiMsgAbort:
		case kScsiMsgAbortTag:
			if (gActiveCommand != NULL && gActiveCommand->scsiReq != NULL) {
				/*
					* 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.
					*/
				if (gCurrentTarget != kInvalidTarget) {
					ddmError("Target %d.%d aborted request\n",
						gCurrentTarget,
						gCurrentLUN,
						3, 4, 5
					);
#if LOG_ERROR_RECOVERY
					IOLog("%s: Target %d.%d aborted request\n",
						[self name],
						gCurrentTarget,
						gCurrentLUN
					);
#endif /* LOG_ERROR_RECOVERY */
				}
		[self ioComplete
			    : gActiveCommand
		    finalStatus : SR_IOST_HW
		];
				gFlagCheckForAnotherInterrupt = FALSE;
				ASSERT(gActiveCommand == NULL);
			}
			/*
			 * After receiving an Abort, the target will go free
			 */
			/* gFlagBusBusy = FALSE;	-- Radar 1678545: wait for interrupt */
			gBusState = SCS_WAIT_FOR_BUS_FREE;
			break;
		case kScsiMsgLinkedCmdComplete:
		case kScsiMsgLinkedCmdCompleteFlag:
			ASSERT(gActiveCommand != NULL && gActiveCommand->scsiReq != NULL);
			/*
			 * 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.
			 */
			IOLog("%s: Target %d.%d terminated with unexpected linked command complete\n",
				[self name],
				gActiveCommand->scsiReq->target,
				gActiveCommand->scsiReq->lun
			);
	    [self ioComplete
			: gActiveCommand
		finalStatus : SR_IOST_HW
	    ];
			ASSERT(gActiveCommand == NULL);
			/* gFlagBusBusy = FALSE;	-- Radar 1678545: wait for interrupt */
			gBusState = SCS_WAIT_FOR_BUS_FREE;
			break;
		case kScsiMsgExtended:
			ASSERT(gActiveCommand != NULL && gActiveCommand->scsiReq != NULL);
			/*
			 * The only expected extended message is synchronous negotiation.
			 * We don't support this yet.
			 */
			switch (gMsgInBuffer[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.)
				 */
		CURIOmessageReject();
				messageAckNeeded = FALSE;
				break;
			default:
				ddmError("Unexpected Extended Message (0x%x) received"
						"from target %d.%d\n",
					gMsgInBuffer[2],
					gActiveCommand->scsiReq->target,
					gActiveCommand->scsiReq->lun,
					4, 5
				);
		CURIOmessageReject();
				messageAckNeeded = FALSE;
				break;
			}
			break;
		default:
			/*
			 * all others are unacceptable. 
			 */
			ddmError("Unsupported message (0x%x) received"
					" from target %d.%d \n", 
				gMsgInBuffer[0],
				gCurrentTarget,
				gCurrentLUN,
				4, 5
			);
	    CURIOmessageReject();
			messageAckNeeded = FALSE;
		} /* Message byte switch */
		if (messageAckNeeded) {
	    CURIOmessageAccept();
		}
		if (ioReturn != IO_R_SUCCESS && gCurrentTarget != kInvalidTarget) {
			ddmError("Message from target %d.%d failed: %s\n",
				gCurrentTarget,
				gCurrentLUN,
				reason,
				4, 5
			);
		}
		RESULT(ioReturn);
		return (ioReturn);
}

/*
 * This writes the message byte into the staging buffer. When the target
 * cycles to MSGO phase, we will send the message from the state automaton.
 */
- (void) putMessageOutByte
						: (UInt8)	messageByte
			setATN		: (Boolean) setATN
{
		ENTRY("Bmo putMessageOutByte");
		ASSERT(gMsgOutPtr < &gMsgOutBuffer[kMessageOutBufferLength]);
		ASSERT(gMsgPutPtr <= gMsgOutPtr);
		*gMsgOutPtr++ = messageByte;
		if (setATN) {
	    CURIOsetATN();
		}
		EXIT();
}

/*
 * Just completed the SCMD_TRANSFER_INFO operation for message out. 
 */
- (void) fsmSendingMsg
{
		ENTRY("Bms fsmSendingMsg");
		ddmChip("fsmSendingMsg\n", 1,2,3,4,5);
		ASSERT(gActiveCommand != NULL && gActiveCommand->scsiReq != NULL);
		EXIT();
}

/*
 * Just completed the SCMD_TRANSFER_INFO operation for command.
 */
- (void) fsmSendingCmd
{
		ENTRY("Bmc fsmSendingCmd");
		ddmChip("fsmSendingCmd\n", 1,2,3,4,5);
		ASSERT(gActiveCommand != NULL && gActiveCommand->scsiReq != NULL);
		gBusState = SCS_INITIATOR;
		EXIT();
}

/*
 * Continue to process reselection interrupts. We expect to be in
 * message in phase in order to get the tagged queue message. Anything
 * else is an error.
 */
- (void) fsmReselecting
{
	 	ENTRY("Bre fsmReselecting");

		[self fsmGettingMsg];
		if (gBusState == SCS_GETTINGMSG) {
			gBusState = SCS_RESELECTING;	/* continue at fsmReselectinAction */
		}
		EXIT();
}

/*
 * Complete the processing of a reselection interrupt. We have just ack'ed
 * 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 in the
 * fullness of time.
 */
- (void) fsmReselectionAction
{
		IOReturn			ioReturn;

		ENTRY("Bra fsmReselectionAction");
		ioReturn = [self reselectNexusWithoutTag];
		if (ioReturn == IO_R_SUCCESS) {
			/*
			 * We have successfully reselected this target.
			 * Vamp until the target tells us what to do next.
			 */
		//	[self fsmPhaseChange];	// mlj ???
			gBusState = SCS_INITIATOR;
		}
		else {
			/*
			 * 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.
			 */
			gCurrentBusPhase = gSaveStatus & mPhase;
			// [curioQuickCheckForChipInterrupt];
			if (gCurrentBusPhase == kBusPhaseMSGI) {
		CURIOstartMSGIAction();
			}
			else {
				/*
				 * We're in the wrong phase. Bail out.
				 */
				[self fsmStartErrorRecovery
								: ioReturn
						reason	: "Unexpected SCSI bus phase while reselecting"
				];
			}
		}
		EXIT();
}


/*
 * Follow SCSI Phase change. Called while SCS_INITIATOR. 
 */
- (void) fsmPhaseChange
{
		int 				i;

		ENTRY("Bph fsmPhaseChange");
		ddmChip("fsmPhaseChange, target %d.%d\n", 
			gCurrentTarget,
			gCurrentLUN,
			3, 4, 5
		);
		gCurrentBusPhase = gSaveStatus & mPhase;
		ASSERT(gActiveCommand != NULL && gActiveCommand->scsiReq != NULL);
		ASSERT(gCurrentTarget	== gActiveCommand->scsiReq->target);
		ASSERT(gCurrentLUN		== gActiveCommand->scsiReq->lun);
		ddmChip("fsmPhaseChange:  phase = %s\n", 
			IOFindNameForValue(gCurrentBusPhase, scsiPhaseValues),
			2,3,4,5
		);
		switch (gCurrentBusPhase) {
		case kBusPhaseCMD:
			/*
			 * The normal case here is after a host-initiated SDTR sequence. 
			 */
	    CURIOflushFifo();
	    CURIOputCommandIntoFifo();
	    CURIOstartNonDMATransfer();
			gMsgInState = kMsgInIdle;
			gBusState = SCS_SENDINGCMD;
			break;
		case kBusPhaseDATI:	/* From target to Initiator (read)	*/
		case kBusPhaseDATO:	/* To Target from Initiator (write) */
			gMsgInState = kMsgInIdle;
			[self fsmStartDataPhase];
			gFlagCheckForAnotherInterrupt = 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.
			 */
			gMsgInState = kMsgInIdle;
			gBusState = SCS_COMPLETING;
	    CURIOflushFifo();
	    CURIOinitiatorCommandComplete();
			break;
		case kBusPhaseMSGI:	/* Message from Target to Initiator */
			gBusState = SCS_GETTINGMSG;
	    CURIOstartMSGIAction();
			break;
		case kBusPhaseMSGO:	/* Message from Initiator to Target */
			gMsgInState = kMsgInIdle;
	    CURIOflushFifo();
			if (gMsgPutPtr == gMsgOutPtr) {
				/*
					* Hmm, there is no message waiting to be sent.
					*/
				*gMsgOutPtr++ = kScsiMsgNop;
				ddmChip("msg out (%d.%d): forced nop\n",
					gCurrentTarget,
					gCurrentLUN,
					3, 4, 5
				);
			}
			for (i = 0; i < 16 && gMsgPutPtr < gMsgOutPtr; i++) {
				ddmChip("msg out[%d] = %02x\n", i, *gMsgPutPtr, 3, 4, 5);
		CURIOputByteIntoFifo(*gMsgPutPtr);
		gMsgPutPtr++;
			}
			if (gMsgPutPtr == gMsgOutPtr) {
				gMsgPutPtr = gMsgOutPtr = gMsgOutBuffer;
			}
			gBusState = SCS_SENDINGMSG;
			/* 
			 * ATN is automatically cleared when transfer info completes.
			 */
	    CURIOstartNonDMATransfer();
			break;
		default:
			[self fsmStartErrorRecovery
							: SR_IOST_HW
					reason	: "Strange bus phase"
			];
			break;
		}
		EXIT();
}

- (void) fsmStartDataPhase
{
		UInt32 				phase;
		IOReturn	 		ioReturn	= IO_R_SUCCESS;
		const char			*reason		= NULL;
		Boolean				isReadOK;


		ENTRY("Bdp startDataPhase");
		ASSERT(gActiveCommand != NULL && gActiveCommand->scsiReq != NULL);
		phase = gSaveStatus & mPhase;
		/*
		 * Data in phase is legal if this is a read command or we're
		 * doing autosense.
		 */
		isReadOK = (gActiveCommand->scsiReq->read || gActiveCommand->flagIsAutosense);
		if ((phase == kBusPhaseDATI) != isReadOK) {
			ioReturn = SR_IOST_BV;
			reason = "bad I/O direction";
		}
		if (ioReturn == IO_R_SUCCESS) {
			ioReturn = [self hardwareInitializeCCL];
			if (ioReturn != IO_R_SUCCESS) {
				reason = "Cannot setup DBDMA Channel Command area";
			}
		}
		if (ioReturn == IO_R_SUCCESS) {
			if (gActiveCommand->thisTransferLength == 0) {
				ioReturn = IO_R_BV;
				reason = "No data for transfer";
			}
		}
		if (ioReturn == IO_R_SUCCESS) {
			DBDMASetCommandPtr((UInt32) gDBDMAChannelAddress);
	    DBDMAstartTransfer();
	    CURIOstartDMATransfer(gActiveCommand->thisTransferLength);
			gBusState = SCS_DMACOMPLETE;
		}
		if (ioReturn != IO_R_SUCCESS) {
			[self fsmStartErrorRecovery
							: ioReturn
					reason	: "Illegal Data Phase (wrong direction or length error)"
			];
		}
		EXIT();
}

/*
 * 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)			fsmStartErrorRecovery
						: (sc_status_t) status
			reason		: (const char *) reason
{
		ENTRY("Bes fsmStartErrorRecovery");
		TAG(__tag__, status);
		ddmChip("fsmStartErrorRecovery for %d.%d, phase \"%s\", status \"%s\": %s\n",
			gCurrentTarget,
			gCurrentLUN,
			IOFindNameForValue(gCurrentBusPhase, scsiPhaseValues),
			IOFindNameForValue(status, IOScStatusStrings),
			(reason == NULL) ? "unspecified" : reason
		);
#if LOG_ERROR_RECOVERY
		if (gActiveCommand != NULL
		 && gActiveCommand->scsiReq != NULL
		 && gActiveCommand->scsiReq->target == gCurrentTarget
		 && gActiveCommand->scsiReq->lun == gCurrentLUN) {
			IOLog("%s: Error recovery: active target %d.%d, error \"%s\" (\"%s\"), bus phase %s\n",
				[self name],
				gCurrentTarget,
				gCurrentLUN,
				IOFindNameForValue(status, IOScStatusStrings),
				(reason == NULL) ? "no further explanation" : reason,
				IOFindNameForValue(gCurrentBusPhase, scsiPhaseValues)
			);
		}
		else if (gActiveCommand != NULL && gActiveCommand->scsiReq != NULL) {
			IOLog("%s: Error recovery: command target %d.%d, error \"%s\" (\"%s\"), bus phase %s\n",
				[self name],
				gActiveCommand->scsiReq->target,
				gActiveCommand->scsiReq->lun,
				IOFindNameForValue(status, IOScStatusStrings),
				(reason == NULL) ? "no further explanation" : reason,
				IOFindNameForValue(gCurrentBusPhase, scsiPhaseValues)
			);
		}
		else if (gCurrentTarget != kInvalidTarget && gCurrentLUN != kInvalidLUN) {
			IOLog("%s: Error Recovery: expected target %d.%d, error \"%s\" (\"%s\"), bus phase %s\n",
				[self name],
				gCurrentTarget,
				gCurrentLUN,
				IOFindNameForValue(status, IOScStatusStrings),
				(reason == NULL) ? "no further explanation" : reason,
				IOFindNameForValue(gCurrentBusPhase, scsiPhaseValues)
			);
		}
		else if (gCurrentTarget != kInvalidTarget && gCurrentLUN == kInvalidLUN) {
			IOLog("%s: Error recovery: command target %d.[no lun], error \"%s\" (\"%s\"), bus phase %s\n",
				[self name],
				gCurrentTarget,
				IOFindNameForValue(status, IOScStatusStrings),
				(reason == NULL) ? "no further explanation" : reason,
				IOFindNameForValue(gCurrentBusPhase, scsiPhaseValues)
			);
		}
		else {
			IOLog("%s: Error recovery: no current target, error \"%s\" (\"%s\"), bus phase %s\n",
				[self name],
				IOFindNameForValue(status, IOScStatusStrings),
				(reason == NULL) ? "no further explanation" : reason,
				IOFindNameForValue(gCurrentBusPhase, scsiPhaseValues)
			);
		}
		[self logRegisters : FALSE reason : "At error recovery start"];
#endif /* LOG_ERROR_RECOVERY */
		[self killActiveCommand : status];
		/*
		 * Clean out the scsi and dbdma chips
		 */
	CURIOflushFifo();
	CURIOclearTransferCountZeroBit();
	DBDMAreset();
		if (gFlagBusBusy == FALSE) {
			gBusState = SCS_DISCONNECTED;
		}
		else {
			/*
			 * We will continue at fsmContinueErrorRecovery
			 */
			gBusState = SCS_DEATH_MARCH;
		}
		gFlagCheckForAnotherInterrupt = FALSE;
		EXIT();
}

/*
 * 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) fsmErrorRecoveryInterruptService
{
		ENTRY("Bei fsmErrorRecoveryInterruptService");
#if LOG_ERROR_RECOVERY
		[self logRegisters : FALSE reason : "At error recovery interrupt"];
		IOLog("%s: Error recovery interrupt in bus phase %s, sts %02x, int %02x\n",
			[self name],
			IOFindNameForValue(gSaveStatus & mPhase, scsiPhaseValues),
			gSaveStatus,
			gSaveInterrupt
		);
 #endif
	CURIOflushFifo();
		switch (gCurrentBusPhase) {
		case kBusPhaseDATO:
		case kBusPhaseDATI:
#if LOG_ERROR_RECOVERY
			IOLog("%s: Error recovery interrupt in data in/out phase\n", [self name]);
#endif
	    DBDMAreset();
	    CURIOclearTransferCountZeroBit();
			break;
		case kBusPhaseCMD:
#if LOG_ERROR_RECOVERY
			IOLog("%s: Error recovery interrupt in command phase\n", [self name]);
#endif
			break;
		case kBusPhaseSTS:
#if LOG_ERROR_RECOVERY
			IOLog("%s: Error recovery interrupt in status phase\n", [self name]);
#endif
			if ((gSaveInterrupt & iFuncComp) != 0) {
#if LOG_ERROR_RECOVERY
				IOLog("%s: Error recovery interrupt in status phase with complete msg\n",
					[self name]
				);
#endif
				/*
				 * We got both bytes: ACK the command complete
				 */
		CURIOmessageAccept();
			}
 			break;
		case kBusPhaseMSGO:
#if LOG_ERROR_RECOVERY
			IOLog("%s: Error recovery interrupt in message out phase\n", [self name]);
#endif
			break;
		case kBusPhaseMSGI:
#if LOG_ERROR_RECOVERY
			IOLog("%s: Error recovery interrupt in message in phase\n", [self name]);
#endif
	    CURIOmessageAccept();
			break;
		case kBusPhaseBusFree:	/* Selecting */
#if LOG_ERROR_RECOVERY
			IOLog("%s: Error recovery interrupt at bus free\n", [self name]);
#endif
			break;
		default:
#if LOG_ERROR_RECOVERY
			IOLog("%s: Strange bus phase %d in error recovery\n",
				[self name],
				gCurrentBusPhase
			);
#endif
			[self killActiveCommandAndResetBus
						: SR_IOST_HW
				reason	: "Strange SCSI phase in error recovery interrupt"
			];
			break;
		}
		EXIT();
}

/*
 * 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)			fsmContinueErrorRecovery
{
		UInt8			msgByte;

		ENTRY("Bec fsmContinueErrorRecovery");
	CURIOquickCheckForChipInterrupt();
#if LOG_ERROR_RECOVERY
		IOLog("%s: Error recovery continues in bus phase %s, sts %02x, int %02x\n",
			[self name],
			IOFindNameForValue(gSaveStatus & mPhase, scsiPhaseValues),
			gSaveStatus,
			gSaveInterrupt
		);
#endif
		if (gFlagBusBusy) {
	    (void) CURIOinterruptPending();
			if ((gSaveInterrupt & iDisconnect) != 0) {
				gFlagBusBusy = FALSE;
			}
		}
		if (gFlagBusBusy == FALSE) {
			gBusState = SCS_DISCONNECTED;
		}
		else {
			gCurrentBusPhase = gSaveStatus & mPhase;
			switch (gCurrentBusPhase) {
			case kBusPhaseMSGO:
				msgByte = kScsiMsgAbort;
				if (gActiveCommand != NULL
				 && gActiveCommand->queueTag != QUEUE_TAG_NONTAGGED) {
					msgByte = kScsiMsgAbortTag;
				}
		CURIOputByteIntoFifo(msgByte);
		CURIOstartNonDMATransfer();
				break;
			case kBusPhaseDATO:
			case kBusPhaseCMD:
		CURIOtransferPad(TRUE);		    /* Output pad   */
				break;
			case kBusPhaseMSGI:
			case kBusPhaseDATI:
			case kBusPhaseSTS:
		CURIOtransferPad(FALSE);	    /* Input pad    */
				break;
			default:
				[self killActiveCommandAndResetBus
						: SR_IOST_HW
					reason	: "Strange SCSI phase in error recovery"
				];
				break;
			}
		}
#if LOG_ERROR_RECOVERY
		[self logRegisters : FALSE reason : "Registers at error recovery action exit"];
#endif /* -Debug */
		EXIT();
}

/*
 * Disable specified mode for gActiveCommand's target. If mode is currently 
 * enabled, we'll log a message to the console.
 */
- (void) disableMode : (TargetMode) mode
{
		int					target;
		PerTargetData		*perTargetPtr;
		const char			*modeStr = NULL;

		ENTRY("Bdm disableMode");
		ASSERT(gActiveCommand != NULL && gActiveCommand->scsiReq != NULL);
		target = gActiveCommand->scsiReq->target;
		perTargetPtr = &gPerTargetData[target];
		switch (mode) {
		case kTargetModeCmdQueue:
			if (perTargetPtr->cmdQueueDisable == 0) {
				perTargetPtr->cmdQueueDisable = 1;
				modeStr = "Command Queueing";
			}
			break;
		default:
			IOLog("%s: Bogus target mode %d for target %d\n",
				[self name],
				mode,
				target
			);
			break;
		}
		ddmChip("DISABLING %s for target %d\n", modeStr, target, 3,4,5);
		if (modeStr != NULL) {
			IOLog("%s: DISABLING %s for target %d\n",
				[self name],
				modeStr,
				target
			);
		}
		EXIT();
}

/**
 * Validate the target's reselection byte (put on the bus before
 * reselecting us). Erase the initiator ID and convert the other
 * bit into an index. The algorithm should be faster than a
 * sequential search, but it probably doesn't matter much.
 * @return	TRUE if successful (gCurrentTarget is now valid).
 *			This function does not check whether there actually
 *			is a command pending for this target.
 */
- (IOReturn) getReselectionTargetID
						: (UInt8) selectByte
{
		IOReturn			ioReturn	= SR_IOST_BV;
		register UInt8		targetBits	= selectByte;
		register UInt8		targetID	= 0;
		register UInt8		bitValue	= 0;	/* Supress warning	*/

		ENTRY("Brt getReselectionTargetID");
		if ((targetBits & gInitiatorIDMask) == 0) {
			IOLog("%s: Reselection failed: initiator ID bit not set, got %02x\n",
				[self name],
				selectByte
			);
		}
		else {
			targetBits &= ~gInitiatorIDMask;			/* Remove our bit			*/
			if (targetBits == 0) {
				IOLog("%s: Reselection failed: target ID bit not set, got %02x\n",
					[self name],
					selectByte
				);
			}
			else {
				bitValue		= targetBits;
				if ((bitValue > 0x0F) != 0) {
					targetID	+= 4;
					bitValue	>>= 4;
				}
				if ((bitValue > 0x03) != 0) {
					targetID	+= 2;
					bitValue	>>= 2;
				}
				if ((bitValue > 0x01) != 0) {
					targetID	+= 1;
				}
		targetBits	&= ~(1 << targetID);	/* Remove the target mask   */
				if (targetBits == 0) {					/* Was exactly one set?		*/
					ioReturn = IO_R_SUCCESS;			/* Yes: success!			*/
		    gCurrentTarget = targetID;		/* Save the current target  */
					ddmChip("Reselection from target %d\n",
						gCurrentTarget,
						2, 3, 4, 5
					);
				}
				else {
					IOLog("%s: Reselection failed, multiple targets, got %02x\n",
						[self name],
						selectByte
					);
				}
			}
		}
		RESULT(ioReturn);
		return (ioReturn);
}

@end	/* Apple96_SCSI(BusState) */