/*
 * Copyright (c) 1998-2002 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@
 */


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	Includes
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

#include "IOFireWireSerialBusProtocolTransport.h"

// IOFireWireFamily includes
#include <IOKit/firewire/IOConfigDirectory.h>
#include <IOKit/firewire/IOFireWireDevice.h>


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	Macros
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

#define DEBUG 												0
#define DEBUG_ASSERT_COMPONENT_NAME_STRING					"FireWire SBP Transport"

#if DEBUG
#define FIREWIRE_SBP_TRANSPORT_DEBUGGING_LEVEL				0
#endif

#if ( FIREWIRE_SBP_TRANSPORT_DEBUGGING_LEVEL >= 1 )
#define PANIC_NOW(x)		IOPanic x
#else
#define PANIC_NOW(x)
#endif

#if ( FIREWIRE_SBP_TRANSPORT_DEBUGGING_LEVEL >= 2 )
#define ERROR_LOG(x)		IOLog x
#else
#define ERROR_LOG(x)
#endif

#if ( FIREWIRE_SBP_TRANSPORT_DEBUGGING_LEVEL >= 3 )
#define STATUS_LOG(x)		IOLog x
#else
#define STATUS_LOG(x)
#endif


#define super IOSCSIProtocolServices
OSDefineMetaClassAndStructors (	IOFireWireSerialBusProtocolTransport, IOSCSIProtocolServices )


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	Constants
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

#define kSBP2ReceiveBufferByteCountKey		"SBP2ReceiveBufferByteCount"
#define kDefaultIOBlockCount				256
#define kCRSModelInfo_ValidBitsMask			0x00FFFFFF
#define kCRSModelInfo_TargetDiskMode		0x0054444D


#if 0
#pragma mark -
#pragma mark ₯ Public Methods
#pragma mark -
#endif


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ init - Called by IOKit to initialize us.						   [PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

bool 
IOFireWireSerialBusProtocolTransport::init ( OSDictionary * propTable )
{
	
	fORB					= 0;
	fLogin					= 0;
	fLoginRetryCount		= 0;
	fReconnectCount			= 0;
	fLoggedIn				= false;
	fPhysicallyConnected	= true;
	fNeedLogin				= false;
	fLUNResetORB			= NULL;
	fDeferRegisterService	= true;
	fObjectIsOpen			= false;
	
	if ( super::init ( propTable ) == false )
	{
		
		return false;
		
	}
	
	return true;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ start - Called by IOKit to start our services.				   [PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

bool 
IOFireWireSerialBusProtocolTransport::start ( IOService * provider )
{
	
	IOReturn	status			= kIOReturnSuccess;
	Boolean		returnValue		= false;
	Boolean 	openSucceeded	= false;
	
	fSBPTarget = OSDynamicCast ( IOFireWireSBP2LUN, provider );
	if ( fSBPTarget == NULL ) goto ErrorExit;
	
	if ( super::start ( provider ) == false ) goto ErrorExit;
	
	if ( provider->open ( this ) == false ) goto ErrorExit;
	
	openSucceeded = true;
	
	fUnit = fSBPTarget->getFireWireUnit ( );
	if ( fUnit == NULL ) goto ErrorExit;
	
	// enable retry on ack d flag
	fUnit->setNodeFlags ( kIOFWEnableRetryOnAckD );
	
	status = AllocateResources ( );
	if ( status != kIOReturnSuccess ) goto ErrorExit;
	
	// get us on the workloop so we can sleep the start thread
	fCommandGate->runAction ( ConnectToDeviceStatic );
	
	if ( reserved->fLoginState == kLogginSucceededState )
	{
		
		registerService ( );
		
	}
	
	STATUS_LOG ( ( "%s: start complete\n", getName ( ) ) );
	
	returnValue = true;
	
	InitializePowerManagement ( provider );
	
	
ErrorExit:
	
	
	if ( returnValue == false )
	{
		
		STATUS_LOG ( ( "%s: start failed.  status = %x\n", getName ( ), status) );
		
		// call the cleanUp method to clean up any allocated resources.
		cleanUp ( );
		
		// close SBP2 if we have opened it.
		if ( ( fSBPTarget != NULL ) && openSucceeded )
		{
			
			fSBPTarget->close ( this );
			fSBPTarget = NULL;
			
		}
		
	}
	
	return returnValue;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ cleanUp - Called to deallocate any resources.					   [PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

void
IOFireWireSerialBusProtocolTransport::cleanUp ( )
{
	
	STATUS_LOG ( ( "%s: cleanUp called\n", getName ( ) ) );
	
	if ( fSBPTarget != NULL )
	{
		
		DeallocateResources ( );
		
	}
	
}


#if 0
#pragma mark -
#pragma mark ₯ Protected Methods
#pragma mark -
#endif


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ CommandORBAccessor - Retrieves command orb.					[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOFireWireSBP2ORB *
IOFireWireSerialBusProtocolTransport::CommandORBAccessor ( void )
{
	
	return fORB;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ SBP2LoginAccessor - Retrieves login orb.						[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOFireWireSBP2Login *
IOFireWireSerialBusProtocolTransport::SBP2LoginAccessor ( void )
{

	return fLogin;

}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ message - Called by IOKit to deliver messages.				[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
IOFireWireSerialBusProtocolTransport::message (	UInt32		type,
												IOService *	nub,
												void *		arg )
{
	
	SBP2ClientOrbData *		clientData 	= NULL;
	IOReturn				status		= kIOReturnSuccess;
	
	switch ( type )
	{
		
		case kIOMessageServiceIsSuspended:
			STATUS_LOG ( ( "%s: kIOMessageServiceIsSuspended\n", getName ( ) ) );
			
			fLoggedIn = false;
			
			break;
			
		case kIOMessageServiceIsResumed:
			STATUS_LOG ( ( "%s: kIOMessageServiceIsResumed\n", getName ( ) ) );
			
			fPhysicallyConnected = true;
			
			if ( fNeedLogin )
			{
				
				STATUS_LOG ( ( "%s: fNeedLogin submitLogin\n", getName ( ) ) );
				
				fNeedLogin = false;
				
				// in case we are resumed after a terminate - check fLogin
				if ( fLogin != NULL )
				{
					fLogin->submitLogin ( );
				}
				
			}
			
			break;
			
		case kIOMessageFWSBP2ReconnectComplete:
			STATUS_LOG ( ( "%s: kIOMessageFWSBP2ReconnectComplete\n", getName ( ) ) );
			
			fLoggedIn = true;
			
			clientData = ( SBP2ClientOrbData * ) fORB->getRefCon ( );
			if ( clientData != NULL )
			{
				
				if ( ( clientData->scsiTask != NULL ) && ( fReconnectCount < kMaxReconnectCount ) )
				{
					
					STATUS_LOG ( ( "%s: resubmit orb \n", getName ( ) ) );
					fReconnectCount++;
					fLogin->submitORB ( fORB );
					
				}
				
				else
				{
					
					// We are unable to recover from bus reset storm
					// We have exhausted the fReconnectCount - punt...
					
					clientData->serviceResponse	= kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
					clientData->taskStatus		= kSCSITaskStatus_No_Status;
					
					CompleteSCSITask ( fORB );
					
				}
				
			}
			
			break;
			
		case kIOMessageFWSBP2ReconnectFailed:
			STATUS_LOG ( ( "%s: kIOMessageFWSBP2ReconnectFailed\n", getName ( ) ) );
			
			if ( fPhysicallyConnected )
			{
				
				fLogin->submitLogin( );
				
			}
			
			else
			{
				
				fNeedLogin = true;
				
			}
			
			break;

		case kIOFWMessageServiceIsRequestingClose:
			STATUS_LOG ( ( "%s: kIOMessageServiceIsRequestingClose\n", getName ( ) ) );
			
			// tell our super to message it's clients that the device is gone
			SendNotification_DeviceRemoved ( );
			
			clientData = ( SBP2ClientOrbData * ) fORB->getRefCon ( );
			if ( clientData != NULL )
			{
				
				if ( clientData->scsiTask != NULL )
				{
					
					// We are unable to recover from bus reset storm
					// We have exhausted the fReconnectCount - punt...
					
					clientData->serviceResponse	= kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
					clientData->taskStatus		= kSCSITaskStatus_No_Status;
					
					CompleteSCSITask ( fORB );
					
				}
				
			}
			
			// let go of memory and what not
			cleanUp ( );
			
			// close SBP2 and allow termination to continue
			if ( fSBPTarget != NULL )
			{
				fSBPTarget->close ( this );
			}
			
			// zero out our provider reference
			fSBPTarget = NULL;
			
			break;
			
		case kIOMessageServiceIsTerminated:
			STATUS_LOG ( ( "%s: kIOMessageServiceIsTerminated\n", getName ( ) ) );
			break;
			
		default:
			
			status = IOService::message (type, nub, arg);
			break;
		
	}
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ SendSCSICommand - Converts a SCSITask to an ORB.				[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

bool
IOFireWireSerialBusProtocolTransport::SendSCSICommand (
							SCSITaskIdentifier		request,
							SCSIServiceResponse * 	serviceResponse,
							SCSITaskStatus *		taskStatus )
{
	
	SBP2ClientOrbData *			clientData 			= NULL;
	IOFireWireSBP2ORB *			orb					= NULL;
	SCSICommandDescriptorBlock	cdb					= { 0 };
	UInt8						commandLength		= 0;
	UInt32						commandFlags		= 0;
	UInt32						timeOut				= 0;
	bool						commandProcessed	= true;
	
	STATUS_LOG ( ( "%s: SendSCSICommand called\n", getName ( ) ) );
	
	*serviceResponse	= kSCSIServiceResponse_Request_In_Process;
	*taskStatus			= kSCSITaskStatus_No_Status;
	
	if ( isInactive ( ) )
	{
		
		// device is disconnected - we can not service command request
		*serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
		commandProcessed = false;
		goto ErrorExit;
		
	}
	
	// get an orb from our orb pool without blocking
	orb = ( IOFireWireSBP2ORB * ) reserved->fCommandPool->getCommand ( false );
	
	if ( orb == NULL )
	{

		// We're busy - return false - command will be resent next time CommandComplete is called
		commandProcessed = false;
		goto ErrorExit;
		
	}
	
	clientData = ( SBP2ClientOrbData * ) orb->getRefCon ( );
	if ( clientData == NULL ) goto ErrorExit;
	
	GetCommandDescriptorBlock ( request, &cdb );
	commandLength = GetCommandDescriptorBlockSize ( request );
	
	#if ( FIREWIRE_SBP_TRANSPORT_DEBUGGING_LEVEL >= 3 )
	if ( commandLength == kSCSICDBSize_6Byte )
	{
		
		STATUS_LOG ( ( "cdb = %02x:%02x:%02x:%02x:%02x:%02x\n", cdb[0], cdb[1],
					 cdb[2], cdb[3], cdb[4], cdb[5] ) );
		
	}
	
	else if ( commandLength == kSCSICDBSize_10Byte )
	{
		
		STATUS_LOG ( ( "cdb = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", cdb[0],
					cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8],
					cdb[9] ) );
		
	}
	
	else if ( commandLength == kSCSICDBSize_12Byte )
	{
		
		STATUS_LOG ( ( "cdb = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", cdb[0],
					cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8],
					cdb[9], cdb[10], cdb[11] ) );
		
	}
	#endif /* (FIREWIRE_SBP_TRANSPORT_DEBUGGING_LEVEL >= 3) */
	
	fReconnectCount	= 0;
	commandFlags	= GetDataTransferDirection ( request ) == kSCSIDataTransfer_FromTargetToInitiator ? 
															  kFWSBP2CommandTransferDataFromTarget : 0L;
	
	orb->setCommandFlags (	commandFlags |
							kFWSBP2CommandCompleteNotify |
							kFWSBP2CommandImmediate |
							kFWSBP2CommandNormalORB );
	
	
	clientData->scsiTask = request;
	
	SetCommandBuffers ( orb, request );
	
	orb->setCommandBlock ( cdb, commandLength );
	
	// SBP-2 needs a non-zero timeout to fire completion routines if timeout
	// is not expressed default to 0xFFFFFFFF.
	
	timeOut = GetTimeoutDuration ( request );
	if ( timeOut == 0 )
	{
		timeOut = 0xFFFFFFFF;
	}
	
	orb->setCommandTimeout ( timeOut );
	
	if ( fLoggedIn )
	{
		
		fLogin->submitORB ( orb );
		
	}
	
	
ErrorExit:
	
	
	STATUS_LOG ( ( "%s: SendSCSICommand exit, Service Response = %x\n", getName ( ), *serviceResponse) );
	
	return commandProcessed;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ SetCommandBuffers - Sets the command buffers in the ORB.		[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
IOFireWireSerialBusProtocolTransport::SetCommandBuffers (
								IOFireWireSBP2ORB *		orb,
								SCSITaskIdentifier		request )
{
	
	return orb->setCommandBuffers (	GetDataBuffer ( request ),
								GetDataBufferOffset ( request ),
								GetRequestedDataTransferCount ( request ) );
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ CompleteSCSITask - Complets a task.							[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

void
IOFireWireSerialBusProtocolTransport::CompleteSCSITask (
								IOFireWireSBP2ORB *		orb )
{
	
	SBP2ClientOrbData *	clientData = NULL;
	
	STATUS_LOG ( ( "%s: CompleteSCSITask called\n", getName ( ) ) );
	
	clientData = ( SBP2ClientOrbData * ) orb->getRefCon ( );
	if ( clientData != NULL )
	{
		
		if ( clientData->scsiTask != NULL )
		{
			
			SCSITaskIdentifier		scsiTask		= NULL;
			SCSIServiceResponse		serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
			SCSITaskStatus			taskStatus		= kSCSITaskStatus_No_Status;
			IOByteCount				bytesTransfered = 0;
			
			/*
			 	/!\ WARNING - because SBP-2 can send status information at different
			 	stage of an orb's life ( or send none at all ) the caller of this routine
			 	has determined that the orb is indeed done. So we need to explicitly tell
				SBP-2 to left go of the buffer reference by calling releaseCommandBuffers.
			*/
			
			orb->releaseCommandBuffers ( );
			
			
			if ( clientData->taskStatus == kSCSITaskStatus_GOOD )
			{
				
				bytesTransfered = GetRequestedDataTransferCount ( clientData->scsiTask );
				
			}
			
			SetRealizedDataTransferCount ( clientData->scsiTask, bytesTransfered );
			
			// re-entrancy protection
			scsiTask				= clientData->scsiTask;
			serviceResponse 		= clientData->serviceResponse;
			taskStatus				= clientData->taskStatus;
			clientData->scsiTask	= NULL;
			
			reserved->fCommandPool->returnCommand ( orb );
			
			CommandCompleted ( scsiTask, serviceResponse, taskStatus );
			
		}
		
	}
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ AbortSCSICommand - Aborts an outstanding I/O.					[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

SCSIServiceResponse
IOFireWireSerialBusProtocolTransport::AbortSCSICommand ( SCSITaskIdentifier request )
{
	
	SCSIServiceResponse		serviceResponse = kSCSIServiceResponse_FUNCTION_REJECTED;
	
	STATUS_LOG ( ( "%s: AbortSCSICommand called\n", getName ( ) ) );
	
	return serviceResponse;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ IsProtocolServiceSupported -	Checks for valid protocol services
//									supported by this device.		[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

bool
IOFireWireSerialBusProtocolTransport::IsProtocolServiceSupported (
								SCSIProtocolFeature		feature,
								void *					serviceValue )
{
	
	bool	isSupported = false;
	
	STATUS_LOG ( ( "IOFireWireSerialBusProtocolTransport::IsProtocolServiceSupported called\n" ) );
	
	switch ( feature )
	{
		
		case kSCSIProtocolFeature_CPUInDiskMode:
			
			isSupported = IsDeviceCPUInDiskMode ( );
			
			break;
		
		case kSCSIProtocolFeature_MaximumReadBlockTransferCount:
		case kSCSIProtocolFeature_MaximumWriteBlockTransferCount:
			
			*( UInt32 * ) serviceValue = kDefaultIOBlockCount;
			isSupported = true;
			
			break;
			
		case kSCSIProtocolFeature_MaximumReadTransferByteCount:
			
			OSNumber *		valuePointer;
			
			// If the property SBP2ReceiveBufferByteCount exists we have a FireWire host
			// with the physical unit off and there is a software FIFO. ( i.e. Lynx )
			
			// We should tell clients to deblock on the SBP2ReceiveBufferByteCount.
			// bounds to avoid stalled I/O.
			
			valuePointer = OSDynamicCast ( OSNumber, getProperty ( kSBP2ReceiveBufferByteCountKey, gIOServicePlane ) );
			if ( valuePointer != NULL )
			{
				
				*( UInt64 * ) serviceValue = valuePointer->unsigned32BitValue ( );
				isSupported = true;			
				
			}
			
			break;
			
		case kSCSIProtocolFeature_GetMaximumLogicalUnitNumber:
			
			*( UInt32 * ) serviceValue = kMaxFireWireLUN;
			isSupported = true;
			
			break;
			
		default:
			break;
		
	}
	
	return isSupported;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ HandleProtocolServiceFeature - Handles protocol service features.
//																	[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

bool
IOFireWireSerialBusProtocolTransport::HandleProtocolServiceFeature ( 
								SCSIProtocolFeature		feature, 
								void *					serviceValue )
{
	return false;
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ IsDeviceCPUInDiskMode - 	Checks if the device is a CPU in
//								FireWire Target Disk Mode.			[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

bool
IOFireWireSerialBusProtocolTransport::IsDeviceCPUInDiskMode ( void )
{
	
	UInt32						csrModelInfo 	= 0;
	IOConfigDirectory *			directory		= NULL;
	IOFireWireDevice *			device			= NULL;
	IOReturn					status			= kIOReturnSuccess;
	IOService *					providerService = fUnit->getProvider( );
	bool						isCPUDiskMode	= false;
	
	STATUS_LOG ( ( "%s: IsDeviceCPUInDiskMode was called\n", getName ( ) ) );
	
	if ( providerService == NULL )
	{
		goto ErrorExit;
	}
	
	device = OSDynamicCast ( IOFireWireDevice, providerService );
	if ( device == NULL )
	{
		goto ErrorExit;
	}
	
	status = device->getConfigDirectory ( directory );	
	if ( status == kIOReturnSuccess )
	{
		
		status = directory->getKeyValue ( kCSRModelInfoKey, csrModelInfo );
		
		if ( status == kIOReturnSuccess )
		{
			
			if ( ( csrModelInfo & kCRSModelInfo_ValidBitsMask ) == kCRSModelInfo_TargetDiskMode )
			{
				isCPUDiskMode = true;
			}
			
		}
		
	}
	
	
ErrorExit:
	
	
	STATUS_LOG ( ( "%s: CPU Disk Mode = %d\n", getName ( ), isCPUDiskMode ) );
	
	return isCPUDiskMode;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ StatusNotifyStatic - 	C->C++ glue method.						[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

void
IOFireWireSerialBusProtocolTransport::StatusNotifyStatic (
								void *					refCon,
								FWSBP2NotifyParams * 	params )
{
	
	( ( IOFireWireSerialBusProtocolTransport * ) refCon )->StatusNotify ( params );
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ StatusNotify - Status notify handler.							[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

void 
IOFireWireSerialBusProtocolTransport::StatusNotify (
								FWSBP2NotifyParams * params )
{

	IOFireWireSBP2ORB *		orb				= NULL;
	FWSBP2StatusBlock *		statusBlock 	= NULL;
	SBP2ClientOrbData *		clientData		= NULL;
	SCSI_Sense_Data *		targetData		= NULL;
	UInt8			 		senseData[kSenseDefaultSize + 8] = { 0 };
	
	targetData = ( SCSI_Sense_Data * ) &senseData[0];
	
	if ( ( params->message != NULL ) && ( params->length != 0 ) )
	{
		
		orb			= ( IOFireWireSBP2ORB * ) params->commandObject;
		statusBlock = ( FWSBP2StatusBlock * ) params->message;
		
		if ( orb != NULL )
		{
			
			clientData = ( SBP2ClientOrbData * ) orb->getRefCon( );
			
		}
		
	}
	
	switch ( params->notificationEvent )
	{
		
		case kFWSBP2NormalCommandStatus:
			
			/*
				read the status block detail bits see SBP-2 spec section 5.3
				check the dead bit ( is 'd' field == 1 )
			*/
			
			if ( ( clientData != NULL ) && ( statusBlock->details & 0x08 ) ) 	
			{
				
				SetValidAutoSenseData ( clientData, statusBlock, targetData );
				
				/*
					wait for fetch agent to reset before calling CompleteSCSITask
					which will be called in FetchAgentResetComplete
				*/
				
				fLogin->submitFetchAgentReset ( );
				
			}
			
			else if ( clientData &&
					( ( statusBlock->details & 0x30 ) == 0 ) &&		// ( is 'resp' field == 0 )
					( ( statusBlock->details & 0x07 ) == 1 ) && 	// ( is 'len' field == 1 )
					( statusBlock->sbpStatus == 0 ) )				// ( is 'sbp_status' field == 0 )
			{
				
				clientData->serviceResponse = kSCSIServiceResponse_TASK_COMPLETE;
				clientData->taskStatus		= kSCSITaskStatus_GOOD;
				
				CompleteSCSITask ( orb );
				
				STATUS_LOG ( ( "%s: StatusNotify normal complete \n", getName ( ) ) );
				
			}
			
			else if ( clientData )
			{
				
				SetValidAutoSenseData ( clientData, statusBlock, targetData );
				
				CompleteSCSITask ( orb );
				
				STATUS_LOG ( ( "%s: StatusNotify unexpected error? \n", getName ( ) ) );
				
			}
			
			break;

		case kFWSBP2NormalCommandTimeout:
			STATUS_LOG ( ( "%s: kFWSBP2NormalCommandTimeout \n", getName ( ) ) );

			if ( clientData )
			{
				
				if ( clientData->scsiTask )
				{
					
					clientData->serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
					clientData->taskStatus		= kSCSITaskStatus_No_Status;
					
					CompleteSCSITask ( orb );
					
				}
				
			}
			
			// reset LUN as good measure in case device is wedged
			fLUNResetORB->submit ( );
			
			break;
			
		case kFWSBP2NormalCommandReset:
			STATUS_LOG ( ( "%s: kFWSBP2NormalCommandReset\n", getName ( ) ) );
			
			/*
				kFWSBP2NormalCommandReset - is a misleading definition
				A pending command has failed so we need notify
				the upper layers to complete failed command.
			*/
			
			if ( clientData != NULL )
			{
				
				if ( clientData->scsiTask != NULL )
				{
					
					clientData->serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
					clientData->taskStatus		= kSCSITaskStatus_No_Status;
					
					CompleteSCSITask ( orb );
					
				}
				
			}
			
			break;
			
		default:
			STATUS_LOG ( ( "%s: StatusNotify with unknown notificationEvent\n", getName ( ) ) );
			break;
		
	}
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ SetValidAutoSenseData - Sets any valid sense data.			[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

void 
IOFireWireSerialBusProtocolTransport::SetValidAutoSenseData (
					SBP2ClientOrbData *		clientData,
					FWSBP2StatusBlock *		statusBlock,
					SCSI_Sense_Data *		targetData )
{
	
	UInt8		quadletCount = 0;
	
	clientData->serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
	clientData->taskStatus		= kSCSITaskStatus_No_Status;
	
	quadletCount = ( statusBlock->details & 0x07 ) - 1 ;
	
	// see if we have any valid sense data
	if ( ( statusBlock->details & 0x30 ) == 0 )
	{
		
		clientData->serviceResponse = kSCSIServiceResponse_TASK_COMPLETE;
		clientData->taskStatus		= CoalesceSenseData ( statusBlock, quadletCount, targetData );
		
	}
	
	if ( clientData->taskStatus == kSCSITaskStatus_CHECK_CONDITION )
	{
		
		if ( clientData->scsiTask != NULL )
		{
			
			SetAutoSenseData ( clientData->scsiTask, targetData, kSenseDefaultSize + 8 );
 			
		}
		
	}
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ CoalesceSenseData - Sets sense data in the data buffer.		[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

SCSITaskStatus
IOFireWireSerialBusProtocolTransport::CoalesceSenseData (
					FWSBP2StatusBlock *		sourceData,
					UInt8					quadletCount,
					SCSI_Sense_Data *		targetData )
{
	
	SCSITaskStatus	returnValue			= kSCSITaskStatus_GOOD;
	UInt8 			statusBlockFormat	= 0;
	
	// Pull bits out of SBP-2 status block ( see SBP-2 Annex B section B.2 )
	// and copy them into sense data block ( see SPC-2 section 7.23.2 )
	
	if ( quadletCount > 0 )
	{
		
		statusBlockFormat = ( sourceData->status[0] >> 30 ) & 0x03;
		returnValue = ( SCSITaskStatus ) ( ( sourceData->status[0] >> 24 ) & 0x3F );
		
		if ( statusBlockFormat == 0 ) 
		{
			
			targetData->VALID_RESPONSE_CODE = kSENSE_RESPONSE_CODE_Current_Errors;
			
		}
		
		else if ( statusBlockFormat == 1 )
		{
			
			targetData->VALID_RESPONSE_CODE = kSENSE_RESPONSE_CODE_Deferred_Errors;
			
		}
		
		if ( statusBlockFormat < 2 )
		{
			
			targetData->VALID_RESPONSE_CODE |= ( sourceData->status[0] >> 16 ) & 0x80;
			targetData->ADDITIONAL_SENSE_CODE = ( sourceData->status[0] >> 8 ) & 0xFF;
			targetData->ADDITIONAL_SENSE_CODE_QUALIFIER = sourceData->status[0] & 0xFF;
			targetData->SENSE_KEY = ( sourceData->status[0] >> 16 ) & 0x0F;
			
			// Set the M, E, I
			// M->FileMark, E->EOM, I->ILI
			targetData->SENSE_KEY |= ( ( sourceData->status[0] >> 16 ) & 0x70 ) << 1;
			
			if ( quadletCount > 1 )
			{
				
				targetData->INFORMATION_1 = ( sourceData->status[1] >> 24 ) & 0xFF;
				targetData->INFORMATION_2 = ( sourceData->status[1] >> 16 ) & 0xFF;
				targetData->INFORMATION_3 = ( sourceData->status[1] >> 8 ) & 0xFF;
				targetData->INFORMATION_4 = sourceData->status[1] & 0xFF;
				targetData->ADDITIONAL_SENSE_LENGTH = 6;
				
			}
			
			if ( quadletCount > 2 )
			{
				
				targetData->COMMAND_SPECIFIC_INFORMATION_1 = ( sourceData->status[2] >> 24 ) & 0xFF;
				targetData->COMMAND_SPECIFIC_INFORMATION_2 = ( sourceData->status[2] >> 16 ) & 0xFF;
				targetData->COMMAND_SPECIFIC_INFORMATION_3 = ( sourceData->status[2] >> 8 ) & 0xFF;
				targetData->COMMAND_SPECIFIC_INFORMATION_4 = sourceData->status[2] & 0xFF;
				targetData->ADDITIONAL_SENSE_LENGTH = 6;
				
			}
			
			if ( quadletCount > 3 )
			{
				
				UInt8	count = ( quadletCount - 3 ) * sizeof ( quadletCount );
				
				bcopy ( &sourceData->status[3],
						&targetData->FIELD_REPLACEABLE_UNIT_CODE,
						count );
				
				targetData->ADDITIONAL_SENSE_LENGTH = count + 6;
				
			}
			
		}
		
	}
	
	return returnValue;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ LoginCompletionStatic - 	C->C++ glue method.					[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

void
IOFireWireSerialBusProtocolTransport::LoginCompletionStatic (
								void *							refCon,
								FWSBP2LoginCompleteParams *		params )
{
	
	( ( IOFireWireSerialBusProtocolTransport * ) refCon )->LoginCompletion ( params );
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ LoginCompletion - Login completion handler.					[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

void 
IOFireWireSerialBusProtocolTransport::LoginCompletion (
								FWSBP2LoginCompleteParams *		params )
{
	
	SBP2ClientOrbData *		clientData = NULL;
	
	STATUS_LOG ( ( "%s: LoginCompletion complete \n", getName ( ) ) );
	
	if	( ( params->status == kIOReturnSuccess ) &&				// ( kIOReturnSuccess )
		( ( params->statusBlock->details & 0x30 ) == 0 ) &&		// ( is 'resp' field == 0 )
		  ( params->statusBlock->sbpStatus == 0 ) )				// ( is 'sbp_status' field == 0 )
	{
		
		fLoginRetryCount	= 0;
		fLoggedIn			= true;
		
		if ( reserved->fLoginState == kFirstTimeLoggingInState )
		{
			
			reserved->fLoginState = kLogginSucceededState;
			fCommandGate->commandWakeup ( ( void * ) &reserved->fLoginState );
			
		}
		
		clientData = ( SBP2ClientOrbData * ) fORB->getRefCon ( );
		if ( clientData != NULL )
		{
			
			if ( clientData->scsiTask )
			{
				
				fLogin->submitORB ( fORB );
				
			}
			
		}
		
	}
	
	else
	{
		
		if ( fPhysicallyConnected )
		{
			
			if ( fLoginRetryCount < kMaxLoginRetryCount )
			{
				
				fLoginRetryCount++;
				STATUS_LOG ( ( "%s: resubmitting Login\n", getName ( ) ) );
				
				fLogin->submitLogin ( );
				
			}
			
			else
			{
				
				if ( reserved->fLoginState == kFirstTimeLoggingInState )
				{
					
					reserved->fLoginState = kLogginFailedState;
					
					// wake up sleeping start thread
					fCommandGate->commandWakeup ( ( void * ) &reserved->fLoginState );
					
				}
				
				/*
					device can not be logged into after kMaxLoginRetryCount
					attemptes let's reset the need login flag in case
					the device was unplugged during login
				*/
				
				fNeedLogin = true;
				
			}
			
		}
		
		else
		{
			
			/*
				login failed because existing device fell off
				the bus set flag to relogin
			*/
			
			fNeedLogin = true;
			
		}
		
	}
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ LogoutCompletionStatic - C->C++ glue.							[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

void
IOFireWireSerialBusProtocolTransport::LogoutCompletionStatic (
								void *							refCon,
								FWSBP2LogoutCompleteParams *	params )
{
	
	( ( IOFireWireSerialBusProtocolTransport * ) refCon )->LogoutCompletion ( params );
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ LogoutCompletion - Logout completion handler.					[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

void 
IOFireWireSerialBusProtocolTransport::LogoutCompletion (
								FWSBP2LogoutCompleteParams *	params )
{
	STATUS_LOG ( ( "%s: LogoutCompletion complete \n", getName ( ) ) );
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ UnsolicitedStatusNotifyStatic - C->C++ glue.					[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

void
IOFireWireSerialBusProtocolTransport::UnsolicitedStatusNotifyStatic (
								void *					refCon,
								FWSBP2NotifyParamsPtr	params )
{
	
	( ( IOFireWireSerialBusProtocolTransport * ) refCon )->UnsolicitedStatusNotify ( params );
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ UnsolicitedStatusNotify - Unsolicited status handler.			[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

void
IOFireWireSerialBusProtocolTransport::UnsolicitedStatusNotify (
								FWSBP2NotifyParamsPtr	params )
{
	
	STATUS_LOG ( ( "%s: UnsolicitedStatusNotify called\n", getName ( ) ) );
	
	// parse and handle unsolicited status
	fLogin->enableUnsolicitedStatus ( );
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ FetchAgentResetCompleteStatic - C->C++ glue.					[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

void
IOFireWireSerialBusProtocolTransport::FetchAgentResetCompleteStatic (
								void *		refCon,
								IOReturn 	status )
{
	
	( ( IOFireWireSerialBusProtocolTransport * ) refCon )->FetchAgentResetComplete ( status );
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ FetchAgentResetComplete - Fetch agent reset handler.			[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

void
IOFireWireSerialBusProtocolTransport::FetchAgentResetComplete ( IOReturn status )
{
	
	SBP2ClientOrbData *		clientData = NULL;
	
	STATUS_LOG ( ( "%s: FetchAgentResetComplete called\n", getName ( ) ) );
	
	// When orb chaining is implemented we will notify upper layer
	// to reconfigure device state and resubmitting commands
	
	clientData = ( SBP2ClientOrbData * ) fORB->getRefCon ( );
	if ( clientData != NULL )
	{
		
		if ( clientData->scsiTask != NULL )
		{
			
			CompleteSCSITask ( fORB );
			
		}
		
	}
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ LunResetCompleteStatic - C->C++ glue.							[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

void
IOFireWireSerialBusProtocolTransport::LunResetCompleteStatic (
								void *							refCon,
								IOReturn						status,
								IOFireWireSBP2ManagementORB *	orb )
{
	
	( ( IOFireWireSerialBusProtocolTransport * ) refCon )->LunResetComplete ( status, orb );
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ LunResetComplete - LUN reset completion handler.				[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

void
IOFireWireSerialBusProtocolTransport::LunResetComplete (
								IOReturn						status,
								IOFireWireSBP2ManagementORB *	orb )
{
	
	STATUS_LOG ( ( "%s: LunResetComplete called\n", getName ( ) ) );
	
	fLogin->submitFetchAgentReset ( );
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ ConnectToDeviceStatic - C->C++ glue.							[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
IOFireWireSerialBusProtocolTransport::ConnectToDeviceStatic (
								OSObject *		refCon, 
								void *			val1,
								void *			val2,
								void *			val3,
								void * 			val4 )
{
	
	( ( IOFireWireSerialBusProtocolTransport * ) refCon )->ConnectToDevice ( );
	
	return kIOReturnSuccess;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ ConnectToDevice - Connects to the device.						[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

void 
IOFireWireSerialBusProtocolTransport::ConnectToDevice ( void )
{
	
	STATUS_LOG ( ( "%s: ConnectToDevice called\n", getName ( ) ) );
	
	// avoid double logins during login phase
	fNeedLogin = false;
	
	fLogin->submitLogin ( );
	
	// sleep the start thread - we'll wake it up on login completion
	fCommandGate->commandSleep ( ( void * ) &reserved->fLoginState , THREAD_UNINT );
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ DisconnectFromDevice - Disconnects from device.				[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

void 
IOFireWireSerialBusProtocolTransport::DisconnectFromDevice ( void )
{
	
	STATUS_LOG ( ( "%s: DisconnectFromDevice called\n", getName ( ) ) );
	
	fLoggedIn = false;
	
	// avoid logins during a logout phase
	fNeedLogin = false;
	
	if ( fPhysicallyConnected )
	{
		
		fLogin->submitLogout ( );
		
	}
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ AllocateResources - Allocates resources.						[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
IOFireWireSerialBusProtocolTransport::AllocateResources ( void )
{

	SBP2ClientOrbData *		clientData	= NULL;
	IOReturn 				status		= kIOReturnNoMemory;
	IOWorkLoop * 			workLoop	= NULL;
	
	STATUS_LOG ( ( "%s: AllocateResources called\n", getName ( ) ) );
	
	fLogin = fSBPTarget->createLogin ( );
	if ( fLogin == NULL ) goto ErrorExit;
	
	fORB = fLogin->createORB ( );
	if ( fORB == NULL ) goto ErrorExit;
	
	clientData = ( SBP2ClientOrbData * ) IOMalloc ( sizeof ( SBP2ClientOrbData ) );
	if ( clientData == NULL ) goto ErrorExit;
	
	bzero ( clientData, sizeof ( SBP2ClientOrbData ) );
	clientData->orb	= fORB;
	fORB->setRefCon ( ( void * ) clientData );
	
	fLogin->setLoginFlags ( kFWSBP2ExclusiveLogin );
	fLogin->setLoginRetryCountAndDelayTime ( 32, 1000000 );
	fLogin->setMaxPayloadSize ( kMaxFireWirePayload );
	fLogin->setStatusNotifyProc ( this, StatusNotifyStatic );
	fLogin->setUnsolicitedStatusNotifyProc ( this, UnsolicitedStatusNotifyStatic );
	fLogin->setLoginCompletion ( this, LoginCompletionStatic );
	fLogin->setLogoutCompletion ( this, LogoutCompletionStatic );
	fLogin->setFetchAgentResetCompletion ( this, FetchAgentResetCompleteStatic );
	
	/*
		set BUSY_TIMEOUT register value
		see SBP-2 spec section 6.2
		also see IEEE Std 1394-1995 section 8.3.2.3.5 ( no I am not kidding )
	*/
	
	fLogin->setBusyTimeoutRegisterValue ( kDefaultBusyTimeoutValue );
	
	fLUNResetORB = fSBPTarget->createManagementORB ( this, LunResetCompleteStatic );
	if ( fLUNResetORB == NULL ) goto ErrorExit;
	
	fLUNResetORB->setCommandFunction ( kFWSBP2LogicalUnitReset );
	fLUNResetORB->setManageeCommand ( fLogin );
	
	// allocate expansion data
	
	reserved = ( ExpansionData * ) IOMalloc ( sizeof ( ExpansionData ) );
	if ( reserved == NULL ) goto ErrorExit;
	
	bzero ( reserved, sizeof ( ExpansionData ) );
	
	reserved->fLoginState = kFirstTimeLoggingInState;
	
	workLoop = getWorkLoop ( );
	if ( workLoop == NULL ) goto ErrorExit;
	
	reserved->fCommandPool = IOCommandPool::withWorkLoop ( workLoop );
	if ( reserved->fCommandPool == NULL ) goto ErrorExit;
	
	// enqueue the command in the free list
	
	reserved->fCommandPool->returnCommand ( fORB );
	
	status = kIOReturnSuccess;
	
	
ErrorExit:
	
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ DeallocateResources - Deallocates resources.					[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

void
IOFireWireSerialBusProtocolTransport::DeallocateResources ( void )
{
	
	SBP2ClientOrbData *		clientData = NULL;
	
	STATUS_LOG ( ( "%s: DeallocateResources called\n", getName ( ) ) );
	
	if ( reserved != NULL )
	{
		
		if ( reserved->fCommandPool != NULL )
		{
			
			reserved->fCommandPool->release ( );
			reserved->fCommandPool = NULL;
			
		}
		
		IOFree ( reserved, sizeof ( ExpansionData ) );
		reserved = NULL;
		
	}
	
	// /!\ WARNING - always release orb's before logins
	
	if ( fLUNResetORB != NULL )
	{
		
		fLUNResetORB->release ( );
		fLUNResetORB = NULL;
		
	}
	
	if ( fORB != NULL )
	{
		
		clientData = ( SBP2ClientOrbData * ) fORB->getRefCon ( );
		if ( clientData != NULL )
		{
			
			IOFree ( clientData, sizeof ( SBP2ClientOrbData ) );
			
		}
		
		fORB->release ( );
		fORB = NULL;
		
	}
	
	if ( fLogin != NULL )
	{
		
		fLogin->release ( );
		fLogin = NULL;
		
	}
	
}


#if 0
#pragma mark -
#pragma mark ₯ VTable Padding
#pragma mark -
#endif


// Space reserved for future expansion.
OSMetaClassDefineReservedUnused ( IOFireWireSerialBusProtocolTransport,  1 );
OSMetaClassDefineReservedUnused ( IOFireWireSerialBusProtocolTransport,  2 );
OSMetaClassDefineReservedUnused ( IOFireWireSerialBusProtocolTransport,  3 );
OSMetaClassDefineReservedUnused ( IOFireWireSerialBusProtocolTransport,  4 );
OSMetaClassDefineReservedUnused ( IOFireWireSerialBusProtocolTransport,  5 );
OSMetaClassDefineReservedUnused ( IOFireWireSerialBusProtocolTransport,  6 );
OSMetaClassDefineReservedUnused ( IOFireWireSerialBusProtocolTransport,  7 );
OSMetaClassDefineReservedUnused ( IOFireWireSerialBusProtocolTransport,  8 );
OSMetaClassDefineReservedUnused ( IOFireWireSerialBusProtocolTransport,  9 );
OSMetaClassDefineReservedUnused ( IOFireWireSerialBusProtocolTransport, 10 );
OSMetaClassDefineReservedUnused ( IOFireWireSerialBusProtocolTransport, 11 );
OSMetaClassDefineReservedUnused ( IOFireWireSerialBusProtocolTransport, 12 );
OSMetaClassDefineReservedUnused ( IOFireWireSerialBusProtocolTransport, 13 );
OSMetaClassDefineReservedUnused ( IOFireWireSerialBusProtocolTransport, 14 );
OSMetaClassDefineReservedUnused ( IOFireWireSerialBusProtocolTransport, 15 );
