/*
 *  IOFWUserCommand.cpp
 *  IOFireWireFamily
 *
 *  Created by noggin on Tue May 08 2001.
 *  Copyright (c) 2001 Apple Computer, Inc. All rights reserved.
 *
 */

// public
#import <IOKit/firewire/IOFireWireDevice.h>
#import <IOKit/firewire/IOFireWireFamilyCommon.h>
#import <IOKit/firewire/IOFireWireController.h>

// private
#import "IOFWUserCommand.h"
#import "IOFireWireLib.h"
#import "IOFWUserVectorCommand.h"

OSDefineMetaClassAndAbstractStructors(IOFWUserCommand, OSObject)
OSDefineMetaClassAndStructors(IOFWUserReadCommand, IOFWUserCommand)
OSDefineMetaClassAndStructors(IOFWUserWriteCommand, IOFWUserCommand)
OSDefineMetaClassAndStructors(IOFWUserPHYCommand, IOFWUserCommand)
OSDefineMetaClassAndStructors(IOFWUserCompareSwapCommand, IOFWUserCommand)
OSDefineMetaClassAndStructors(IOFWUserAsyncStreamCommand, IOFWUserCommand)

// ============================================================
// IOFWUserCommand
// ============================================================
void
IOFWUserCommand::free()
{
	if ( fCommand )
	{
		IOReturn	cmdStatus = fCommand->getStatus() ;
		if ( cmdStatus == kIOReturnBusy || cmdStatus == kIOFireWirePending )
		{
			DebugLog("cancelling cmd %p\n", fCommand) ;
			fCommand->cancel( kIOReturnAborted ) ;
		}
		
		fCommand->release() ;
		fCommand = NULL;
	}

	if( fOutputArgs )
	{
		IOFree( fOutputArgs, fOutputArgsSize );
		fOutputArgs = NULL;
		fQuads = NULL ;
		fNumQuads = 0 ;
	}
		
	if (fMem)
	{
		fMem->complete() ;
		fMem->release() ;
		fMem = NULL;
	}
	
	OSObject::free() ;
}

void
IOFWUserCommand::setAsyncReference64(
	OSAsyncReference64	inAsyncRef)
{
	bcopy(inAsyncRef, fAsyncRef, sizeof(OSAsyncReference64)) ;
}

IOFWUserCommand*
IOFWUserCommand::withSubmitParams(
	const CommandSubmitParams*	params,
	const IOFireWireUserClient*			inUserClient)
{
	IOFWUserCommand*	result	= NULL ;
	
	switch ( params->type )
	{
		case kFireWireCommandType_Read:
			// fallthru
		case kFireWireCommandType_ReadQuadlet:
			result = OSTypeAlloc( IOFWUserReadCommand );
			break ;
		
		case kFireWireCommandType_Write:
			// fallthru
		case kFireWireCommandType_WriteQuadlet:
			result = OSTypeAlloc( IOFWUserWriteCommand );
			break ;
			
		case kFireWireCommandType_CompareSwap:
			result = OSTypeAlloc( IOFWUserCompareSwapCommand );
			break ;

		case kFireWireCommandType_PHY:
			result = OSTypeAlloc( IOFWUserPHYCommand );
			break ;
			
		case kFireWireCommandType_AsyncStream:
			result = OSTypeAlloc( IOFWUserAsyncStreamCommand );
			break;
		
		default:
			DebugLog( "bad command type!\n" ) ;
			break ;
	}
	
	if (result && !result->initWithSubmitParams( params, inUserClient ))
	{
		result->release() ;
		result = NULL ;
	}

	return result ;
}

bool
IOFWUserCommand::initWithSubmitParams(
	const CommandSubmitParams*	params,
	const IOFireWireUserClient*			inUserClient)
{
	fFlush = true;
	fUserClient	= inUserClient ;
	return true ;
}


void
IOFWUserCommand::asyncReadWriteCommandCompletion(
	void *					refcon, 
	IOReturn 				status, 
	IOFireWireNub *			device, 
	IOFWCommand *			fwCmd)
{	
	IOFWUserCommand*	cmd = (IOFWUserCommand*) refcon ;

	// tell the vector
	if( cmd->fVectorCommand )
	{
		cmd->fVectorCommand->asyncCompletion( refcon, status, device, fwCmd );
	}
	else if ( refcon && cmd->fAsyncRef[0] ) 
	{
		io_user_reference_t args[3];
		args[0] = cmd->fCommand->getBytesTransferred();
		args[1] = cmd->fCommand->getAckCode();
		args[2] = cmd->fCommand->getResponseCode();

		IOReturn error = kIOReturnSuccess;
		error = IOFireWireUserClient::sendAsyncResult64( cmd->fAsyncRef, status, args, 3 );
		
		DebugLogCond ( error, "IOFWUserCommand::asyncReadWriteCommandCompletion: sendAsyncResult64 returned error %x\n", error ) ;
	}
}

void
IOFWUserCommand::asyncReadQuadletCommandCompletion(
	void *					refcon, 
	IOReturn 				status, 
	IOFireWireNub *			device, 
	IOFWCommand *			fwCmd)
{
	IOFWUserCommand*	cmd = (IOFWUserCommand*)refcon ;

	if (refcon && cmd->fAsyncRef[0] )
	{
		cmd->fOutputArgs[0] = cmd->fCommand->getAckCode();
		cmd->fOutputArgs[1] = cmd->fCommand->getResponseCode();
		// quad data is already in fOutputArgs[3] and later
		
		IOReturn result = kIOReturnSuccess;
		result = IOFireWireUserClient::sendAsyncResult64( cmd->fAsyncRef, status, (io_user_reference_t *)cmd->fOutputArgs, (UInt32)(( cmd->fCommand->getBytesTransferred() >> 2) + 2) ) ;
		DebugLogCond ( result, "IOFireWireUserClient::asyncReadQuadletCommandCompletion: sendAsyncResult64 returned error 0x%08x\n", result) ;
	}
}

#pragma mark -
// ============================================================
// IOFWUserReadCommand
// ============================================================

bool
IOFWUserReadCommand::initWithSubmitParams(
	const CommandSubmitParams*	params,
	const IOFireWireUserClient*			inUserClient)
{
	bool	result = true ;

	result = (0 != IOFWUserCommand::initWithSubmitParams(params, inUserClient)) ;
	
	if (result)
	{
		fMem = IOMemoryDescriptor::withAddressRange( params->newBuffer, params->newBufferSize, kIODirectionIn, fUserClient->getOwningTask() ) ;
		result = (NULL != fMem) ;
	}
	
	if (result)
	{
		IOReturn error = fMem->prepare() ;
		result = ( kIOReturnSuccess == error ) ;
	}
	
	if (!result)
	{
		if (fMem)
		{
			fMem->release() ;
			fMem = NULL;
		}
	}
	
	return result ;
}

IOReturn
IOFWUserReadCommand::submit(
	CommandSubmitParams*	params,
	CommandSubmitResult*	outResult)
{
	IOReturn	error		= kIOReturnSuccess ;
	Boolean		syncFlag 	= ( params->flags & kFWCommandInterfaceSyncExecute ) != 0 ;
	Boolean		copyFlag	= ( params->flags & kFireWireCommandUseCopy ) != 0;
	Boolean		absFlag		= ( params->flags & kFireWireCommandAbsolute ) != 0 ;
	bool		forceBlockFlag	= (params->flags & kFWCommandInterfaceForceBlockRequest) != 0;

	FWAddress target_address;
	target_address.addressLo = (UInt32)(params->newTarget & 0xffffffff);
	target_address.addressHi = (UInt16)((params->newTarget >> 32) & 0x0000ffff);
	target_address.nodeID = (UInt16)(params->newTarget >> 48);

	if ( params->staleFlags & kFireWireCommandStale_Buffer )	// do we need reevaluate our buffers?
	{
		if ( fMem )	// whatever happens, we're going to need a new memory descriptor
		{
			fMem->complete() ;
			fMem->release() ;
			fMem = NULL;
		}
		
		if ( copyFlag )	// is this command using in-line data?
		{
			if( ( fQuads && (fNumQuads != params->newBufferSize ) ) || syncFlag)	// if we're executing synchronously,
																			// don't need quadlet buffer
			{
				IOFree( fOutputArgs, fOutputArgsSize );
				fOutputArgs = NULL;
				fQuads = NULL ;
				fNumQuads = 0 ;
			}
			
			if (!syncFlag)
			{
				fNumQuads = params->newBufferSize ;
				fOutputArgsSize = (params->newBufferSize + 2) * sizeof(UInt32);
				fOutputArgs = (UInt32*)IOMalloc( fOutputArgsSize );
				fQuads = fOutputArgs + 2;
			}
				
			fMem = NULL ;
		}
		else
		{
			if (fQuads)
			{
				IOFree( fOutputArgs, fOutputArgsSize );
				fOutputArgs = NULL;
				fQuads = NULL ;
				fNumQuads = 0 ;
			}
		
			if (NULL == (fMem = IOMemoryDescriptor::withAddressRange( params->newBuffer, 
													params->newBufferSize, 
													kIODirectionIn, 
													fUserClient->getOwningTask())) )
			{
				error = kIOReturnNoMemory ;
			}
			else
			{
				error = fMem->prepare() ;
			
				if ( error )
				{
					fMem->release() ;
					fMem = NULL ;
				}
			}	
		}
	}

	if ( not error )
	{
		if (fCopyFlag != copyFlag)
			if (fCommand)
			{
				fCommand->release() ;	// we had a normal command and need a quadlet command or vice-versa
				fCommand = NULL ;
			}

		if (fCommand)
		{
			if ( copyFlag )
				if (syncFlag)
				{
					if ( absFlag )
					{
						error = ((IOFWReadQuadCommand*)fCommand)->reinit( params->newGeneration, target_address, (UInt32*) params+1, params->newBufferSize, NULL, this ) ;
					}
					else
						error = ((IOFWReadQuadCommand*)fCommand)->reinit( target_address, (UInt32*) params+1, params->newBufferSize, NULL, this, params->newFailOnReset) ;
				}
				else
				{
					error = ((IOFWReadQuadCommand*)fCommand)->reinit( target_address,
																	   fQuads,
																	   fNumQuads,
																	   & IOFWUserCommand::asyncReadWriteCommandCompletion,
																	   this,
																	   params->newFailOnReset) ;
				}
			else
				error = ((IOFWReadCommand*)fCommand)->reinit( target_address,
															fMem, 
															syncFlag ? NULL : & IOFWUserCommand::asyncReadWriteCommandCompletion,
															this,
															params->newFailOnReset ) ;

			fCommand->setGeneration(params->newGeneration) ;			
			DebugLogCond ( error, "IOFWUserReadCommand::submit: fCommand->reinit error=%08x\n", error) ;
		}
		else// if (params->staleFlags )
		{
			if ( copyFlag )
			{
				if (syncFlag)
				{
					if ( absFlag )
					{
						fCommand = fUserClient->createReadQuadCommand( params->newGeneration, target_address, (UInt32*) params+1, params->newBufferSize, NULL, this ) ;
					}
					else
						fCommand = fUserClient->getOwner()->createReadQuadCommand( target_address, (UInt32*) params+1, params->newBufferSize, NULL, this, params->newFailOnReset ) ;
				}
				else															   
				{
					// create a quadlet command and copy in-line quads into it.
					if ( absFlag )
					{
						fCommand = fUserClient->createReadQuadCommand( params->newGeneration, target_address, fQuads, fNumQuads, & IOFWUserCommand::asyncReadQuadletCommandCompletion, this ) ;
					}
					else
					{
						fCommand = fUserClient->getOwner()->createReadQuadCommand( target_address,
																					fQuads,
																					fNumQuads,
																					& IOFWUserCommand::asyncReadQuadletCommandCompletion,
																					this,
																					params->newFailOnReset) ;
						if ( fCommand )
							fCommand->setGeneration( params->newGeneration ) ;
					}
				}
			}
			else
			{
				// create a read command -- memory descriptor based
				if ( absFlag )
				{
					fCommand = fUserClient->createReadCommand( params->newGeneration, target_address, fMem, syncFlag ? NULL : & IOFWUserCommand::asyncReadWriteCommandCompletion, this ) ;
				}
				else
				{
					fCommand = fUserClient->getOwner()->createReadCommand( target_address,
																			fMem,
																			syncFlag ? NULL : & IOFWUserCommand::asyncReadWriteCommandCompletion,
																			this,
																			params->newFailOnReset ) ;
					if ( fCommand )
						fCommand->setGeneration( params->newGeneration ) ;
				}
			}
			
			if (!fCommand)
				error = kIOReturnNoMemory ;
		}
	}
	
	if ( not error )
	{
		if (params->staleFlags & kFireWireCommandStale_MaxPacket)
		{
			fCommand->setMaxPacket(params->newMaxPacket) ;
		}

		if( params->staleFlags & kFireWireCommandStale_Timeout )
		{
			fCommand->setTimeout( params->timeoutDuration );
		}

		if( params->staleFlags & kFireWireCommandStale_Retries )
		{
			fCommand->setRetries( params->retryCount );
		}

		if( params->staleFlags & kFireWireCommandStale_Speed )
		{
			fCommand->setMaxSpeed( params->maxPacketSpeed );
		}

		// block or not
		fCommand->setForceBlockRequests( forceBlockFlag );

		// turn off flushing if requested
		if( !fFlush )
		{
			fCommand->setFlush( fFlush );
		}
		
		error = fCommand->submit() ;
		
		if( !fFlush )
		{
			fCommand->setFlush( true );
		}
		
		DebugLogCond ( error, "IOFWUserReadCommand::submit: fCommand->submit error=%08x\n", error ) ;
	}
						
	if( syncFlag && (outResult != NULL) )
	{
		outResult->result 			= fCommand->getStatus() ;
		outResult->bytesTransferred	= (UInt32)fCommand->getBytesTransferred() ;
		outResult->ackCode			= fCommand->getAckCode();
		outResult->responseCode		= fCommand->getResponseCode();
		
		// mach won't copy any of our result info out on an error, pretend everything is fine
		error = kIOReturnSuccess;
	}	

	return error ;
}

#pragma mark -
// ============================================================
// IOFWUserWriteCommand
// ============================================================
bool
IOFWUserWriteCommand::initWithSubmitParams(
	const CommandSubmitParams*	params,
	const IOFireWireUserClient*			inUserClient)
{
	bool	result = true ;

	result = (0 != IOFWUserCommand::initWithSubmitParams(params, inUserClient)) ;

	if (result)
	{
		fMem = IOMemoryDescriptor::withAddressRange( params->newBuffer, params->newBufferSize, kIODirectionOut, fUserClient->getOwningTask() ) ;
		result = (NULL != fMem) ;
	}

	if (result)
	{
		IOReturn error = fMem->prepare() ;
		result = (error == kIOReturnSuccess) ;
	}

	if (!result)
	{
		if (fMem)
		{
			fMem->release() ;
			fMem = NULL;
		}
	}
	
	return result ;
}

IOReturn
IOFWUserWriteCommand::submit(
	CommandSubmitParams*	params,
	CommandSubmitResult*	outResult)
{
	IOReturn	result		= kIOReturnSuccess;
	Boolean		syncFlag 	= (params->flags & kFWCommandInterfaceSyncExecute) != 0;
	Boolean		copyFlag	= (params->flags & kFireWireCommandUseCopy) != 0;
	Boolean		absFlag		= (params->flags & kFireWireCommandAbsolute) != 0;
	bool		forceBlockFlag	= (params->flags & kFWCommandInterfaceForceBlockRequest) != 0;
	
	FWAddress target_address;
	target_address.addressLo = (UInt32)(params->newTarget & 0xffffffff);
	target_address.addressHi = (UInt16)((params->newTarget >> 32) & 0x0000ffff);
	target_address.nodeID = (UInt16)(params->newTarget >> 48);

	if ( params->staleFlags & kFireWireCommandStale_Buffer )	// do we need reevaluate our buffers?
	{
		if ( fMem )	// whatever happens, we're going to need a new memory descriptor
		{
			fMem->complete() ;
			fMem->release() ;
            fMem = NULL;
		}
		
		if ( copyFlag )	// is this command using in-line data?
			fMem = NULL ;
		else
		{
			if (NULL == (fMem = IOMemoryDescriptor::withAddressRange( params->newBuffer, 
													params->newBufferSize, 
													kIODirectionOut, 
													fUserClient->getOwningTask())) )
			{
				result = kIOReturnNoMemory ;
			}
			else
			{
				result = fMem->prepare() ;
				
				if ( kIOReturnSuccess != result )
				{
					fMem->release() ;
					fMem = NULL ;
				}
			}
		}
	}

	if ( kIOReturnSuccess == result)
	{
		if (fCopyFlag != copyFlag)
			if (fCommand)
			{
				fCommand->release() ;	// we had a normal command and need a quadlet command or vice-versa
				fCommand = NULL ;
			}

		if (fCommand)
		{
			if ( copyFlag )
			{
				result = ((IOFWWriteQuadCommand*)fCommand)->reinit( target_address,
																	(UInt32*) params+1,
																	params->newBufferSize,
																	syncFlag ? NULL : & IOFWUserCommand::asyncReadWriteCommandCompletion,
																	this,
																	params->newFailOnReset) ;
			}
			else
			{
				result = ((IOFWWriteCommand*)fCommand)->reinit( target_address,
															    fMem, 
															    syncFlag ? NULL : & IOFWUserCommand::asyncReadWriteCommandCompletion,
															    this,
															    params->newFailOnReset ) ;
			}
			
			DebugLogCond ( result, "IOFWUserWriteCommand::submit: fCommand->reinit result=%08x\n", result) ;
		}
		else
		{
			if ( copyFlag )
			{
				if ( absFlag )
					fCommand = fUserClient->createWriteQuadCommand( params->newGeneration, target_address, (UInt32*) params+1, params->newBufferSize, syncFlag ? NULL : & IOFWUserCommand::asyncReadWriteCommandCompletion, this ) ;
				else
				{
					fCommand = fUserClient->getOwner()->createWriteQuadCommand( target_address,
																				(UInt32*) params+1,
																				params->newBufferSize,
																				syncFlag ? NULL : & IOFWUserCommand::asyncReadWriteCommandCompletion,
																				this,
																				params->newFailOnReset ) ;
					if ( fCommand )
						fCommand->setGeneration( params->newGeneration ) ;
				}
			}
			else
			{
				// create a read command -- memory descriptor based
				if ( absFlag )
					fCommand = fUserClient->createWriteCommand( params->newGeneration,
																target_address,
																fMem,
																syncFlag ? NULL : & IOFWUserCommand::asyncReadWriteCommandCompletion,
																this ) ;
				else
				{
					fCommand = fUserClient->getOwner()->createWriteCommand( target_address,
																			fMem,
																			syncFlag ? NULL : & IOFWUserCommand::asyncReadWriteCommandCompletion,
																			this,
																			params->newFailOnReset ) ;
					if ( fCommand )
						fCommand->setGeneration( params->newGeneration ) ;
				}
			}
			
			if (!fCommand)
				result = kIOReturnNoMemory ;
		}
	}
	
	if ( kIOReturnSuccess == result)
	{
		if (params->staleFlags & kFireWireCommandStale_MaxPacket)
		{
			fCommand->setMaxPacket(params->newMaxPacket) ;// zzz is there any reason for us to pay
		}
			
		if( params->staleFlags & kFireWireCommandStale_Timeout )
		{
			fCommand->setTimeout( params->timeoutDuration );
		}

		if( params->staleFlags & kFireWireCommandStale_Retries )
		{
			fCommand->setRetries( params->retryCount );
		}

		if( params->staleFlags & kFireWireCommandStale_Speed )
		{
			fCommand->setMaxSpeed( params->maxPacketSpeed );
		}
	
		// block or not
		fCommand->setForceBlockRequests( forceBlockFlag );
		
		// turn off flushing if requested
		if( !fFlush )
		{
			fCommand->setFlush( fFlush );
		}
		
		result = fCommand->submit() ;
		
		if( !fFlush )
		{
			fCommand->setFlush( true );
		}

		DebugLogCond ( result, "IOFWUserReadCommand::submit: fCommand->submit result=%08x\n", result);
	}
						
	if( syncFlag && (outResult != NULL) )
	{
		outResult->result 			= fCommand->getStatus();
		outResult->bytesTransferred	= (UInt32)fCommand->getBytesTransferred();
		outResult->ackCode			= fCommand->getAckCode();
		outResult->responseCode		= fCommand->getResponseCode();

		// mach won't copy any of our result info out on an error, pretend everything is fine
		result = kIOReturnSuccess;
	}	
	
	return result;
}

#pragma mark -
// ============================================================
// IOFWUserPHYCommand
// ============================================================
bool
IOFWUserPHYCommand::initWithSubmitParams(
	const CommandSubmitParams*	params,
	const IOFireWireUserClient*			inUserClient)
{
	bool	result = true ;

	result = (0 != IOFWUserCommand::initWithSubmitParams(params, inUserClient));
	
	return result;
}

IOReturn
IOFWUserPHYCommand::submit(
	CommandSubmitParams*	params,
	CommandSubmitResult*	outResult)
{
	IOReturn	result		= kIOReturnSuccess;
	Boolean		syncFlag 	= (params->flags & kFWCommandInterfaceSyncExecute) != 0;
	
	if( kIOReturnSuccess == result)
	{	
		if( fPHYCommand )
		{
			result = fPHYCommand->reinit(	params->newGeneration,
											params->data1,
											params->data2, 
											syncFlag ? NULL : & IOFWUserPHYCommand::asyncPHYCommandCompletion,
											this,
											params->newFailOnReset );
	
			DebugLogCond ( result, "IOFWUserPHYCommand::submit: fCommand->reinit result=%08x\n", result) ;
		}
		else
		{
			IOFireWireController * control = fUserClient->getOwner()->getController();
			
			fPHYCommand = control->createAsyncPHYCommand(	params->newGeneration,
															params->data1,
															params->data2, 
															syncFlag ? NULL : &IOFWUserPHYCommand::asyncPHYCommandCompletion,
															this,
															params->newFailOnReset );
			
			if( !fPHYCommand )
			{
				result = kIOReturnNoMemory;
			}
		}
	}
	
	if( kIOReturnSuccess == result )
	{
		if( params->staleFlags & kFireWireCommandStale_Timeout )
		{
			fPHYCommand->setTimeout( params->timeoutDuration );
		}

		if( params->staleFlags & kFireWireCommandStale_Retries )
		{
			fPHYCommand->setRetries( params->retryCount );
		}
	
		// turn off flushing if requested
		if( !fFlush )
		{
			fPHYCommand->setFlush( fFlush );
		}
		
		result = fPHYCommand->submit() ;
		
		if( !fFlush )
		{
			fPHYCommand->setFlush( true );
		}

		DebugLogCond ( result, "IOFWUserPHYCommand::submit: fCommand->submit result=%08x\n", result);
	}
						
	if( syncFlag && (outResult != NULL) )
	{
		outResult->result 			= fPHYCommand->getStatus();
		outResult->bytesTransferred	= 8;
		outResult->ackCode			= fPHYCommand->getAckCode();
		outResult->responseCode		= fPHYCommand->getResponseCode();

		// mach won't copy any of our result info out on an error, pretend everything is fine
		result = kIOReturnSuccess;

	}	
	
	return result;
}

void
IOFWUserPHYCommand::free()
{
	if( fPHYCommand )
	{
		IOReturn	cmdStatus = fPHYCommand->getStatus();
		if ( cmdStatus == kIOReturnBusy || cmdStatus == kIOFireWirePending )
		{
			DebugLog("cancelling cmd %p\n", fCommand);
			fPHYCommand->cancel( kIOReturnAborted );
		}
		
		fPHYCommand->release() ;
		fCommand = NULL;
	}
	
	IOFWUserCommand::free();
}

void
IOFWUserPHYCommand::asyncPHYCommandCompletion(
	void *					refcon, 
	IOReturn 				status, 
	IOFireWireBus *			bus, 
	IOFWAsyncPHYCommand *	fwCmd )
{	
	IOFWUserPHYCommand*	cmd = (IOFWUserPHYCommand*) refcon ;

	// tell the vector
	if( cmd->fVectorCommand )
	{
		cmd->fVectorCommand->asyncPHYCompletion( refcon, status, bus, fwCmd );
	}
	else if ( refcon && cmd->fAsyncRef[0] ) 
	{
		io_user_reference_t args[3];
		args[0] = 8;
		args[1] = cmd->fPHYCommand->getAckCode();
		args[2] = cmd->fPHYCommand->getResponseCode();
		
		IOReturn error = kIOReturnSuccess;
		error = IOFireWireUserClient::sendAsyncResult64( cmd->fAsyncRef, status, args, 3 );
		
		DebugLogCond ( error, "IOFWUserCommand::asyncReadWriteCommandCompletion: sendAsyncResult64 returned error %x\n", error ) ;
	}
}

#pragma mark -
// ============================================================
// IOFWUserCompareSwapCommand
// ============================================================
bool
IOFWUserCompareSwapCommand::initWithSubmitParams(
	const CommandSubmitParams*	params,
	const IOFireWireUserClient*			inUserClient)
{
	bool	result = true ;

	result = (0 != IOFWUserCommand::initWithSubmitParams(params, inUserClient)) ;
	
	return result ;
}

IOReturn
IOFWUserCompareSwapCommand::submit ( CommandSubmitParams *	params, CommandSubmitResult * outResult )
{
	// cast to the right type:
	// for compare swap commands we are really dealing with a 'CompareSwapSubmitResult'
	// but we have to override submit() with a prototype matching our superclass submit()
	CompareSwapSubmitResult* result = (CompareSwapSubmitResult*)outResult ;

	IOReturn	error		= kIOReturnSuccess ;

	if ( params->staleFlags & kFireWireCommandStale_Buffer )
		fSize = params->newBufferSize ;

	Boolean			syncFlag 	= ( params->flags & kFWCommandInterfaceSyncExecute ) != 0 ;
	Boolean			absFlag		= ( params->flags & kFireWireCommandAbsolute ) != 0 ;

	FWAddress target_address;
	target_address.addressLo = (UInt32)(params->newTarget & 0xffffffff);
	target_address.addressHi = (UInt16)((params->newTarget >> 32) & 0x0000ffff);
	target_address.nodeID = (UInt16)(params->newTarget >> 48);
		
	if ( params->staleFlags & kFireWireCommandStale )
	{
		if ( fCommand )
		{
			fCommand->release() ;
			fCommand = NULL ;
		}
		
		if ( absFlag )
			fCommand = fUserClient->createCompareAndSwapCommand(	params->newGeneration, 
																	target_address, 
																	(UInt32*)(params+1),// cmpVal past end of param struct
																	(UInt32*)(params+1) + 2,// newVal past end of cmpVal
																	(int)(fSize >> 2),
																	syncFlag ? NULL : & IOFWUserCompareSwapCommand::asyncCompletion,
																	this ) ;
		else
		{
			fCommand = fUserClient->getOwner()->createCompareAndSwapCommand( target_address, 
																			(UInt32*)(params+1),// cmpVal past end of param struct
																			(UInt32*)(params+1) + 2,// newVal past end of cmpVal
																			(int)(fSize >> 2),
																			syncFlag ? NULL : & IOFWUserCompareSwapCommand::asyncCompletion,
																			this,
																			params->newFailOnReset ) ;
			if ( fCommand )
				fCommand->setGeneration( params->newGeneration ) ;
		}
		
		if ( !fCommand )
			error = kIOReturnNoMemory ;

	}
	else //if ( params->staleFlags )
	{
		if ( absFlag )
			error =((IOFWCompareAndSwapCommand*)fCommand)->reinit( params->newGeneration, 
					target_address, (UInt32*)(params+1),// cmpVal past end of param struct
					(UInt32*)(params+1) + 2,// newVal past end of cmpVal
					(int)(fSize >> 2), syncFlag ? NULL : & IOFWUserCompareSwapCommand::asyncCompletion, this ) ;
		else
		{
			error = ((IOFWCompareAndSwapCommand*)fCommand)->reinit( target_address, 
					(UInt32*)(params+1),// cmpVal past end of param struct
					(UInt32*)(params+1) + 2,// newVal past end of cmpVal
					(int)(fSize >> 2), syncFlag ? NULL : & IOFWUserCompareSwapCommand::asyncCompletion, this,
					params->newFailOnReset ) ;
			fCommand->setGeneration( params->newGeneration ) ;
		}
	}

	if ( !error )
	{
		if( params->staleFlags & kFireWireCommandStale_Timeout )
		{
			fCommand->setTimeout( params->timeoutDuration );
		}

		if( params->staleFlags & kFireWireCommandStale_Retries )
		{
			fCommand->setRetries( params->retryCount );
		}

		if( params->staleFlags & kFireWireCommandStale_Speed )
		{
			fCommand->setMaxSpeed( params->maxPacketSpeed );
		}
	
		// turn off flushing if requested
		if( !fFlush )
		{
			fCommand->setFlush( fFlush );
		}
		
		error = fCommand->submit() ;
		
		if( !fFlush )
		{
			fCommand->setFlush( true );  // always restore command to true
		}
							
		if( syncFlag && (result != NULL) )
		{
			result->result 					= fCommand->getStatus() ;
			result->bytesTransferred		= (UInt32)fCommand->getBytesTransferred() ;

			// *** WARNING: Taking address of packed member 'value' of class or structure 'IOFireWireLib::FWCompareSwapLockInfo' may result in an unaligned pointer value
			result->lockInfo.didLock		= ((IOFWCompareAndSwapCommand*)fCommand)->locked( (UInt32*) & result->lockInfo.value ) ;
			result->ackCode					= fCommand->getAckCode();
			result->responseCode			= fCommand->getResponseCode();

			// mach won't copy any of our result info out on an error, pretend everything is fine
			error = kIOReturnSuccess;
		}	
	}
	
	return error ;
}

void
IOFWUserCompareSwapCommand::asyncCompletion(
	void *					refcon, 
	IOReturn 				status, 
	IOFireWireNub *			device, 
	IOFWCommand *			fwCmd)
{
	IOFWUserCompareSwapCommand*	cmd = (IOFWUserCompareSwapCommand*)refcon ;

	if (refcon && cmd->fAsyncRef[0] )
	{
		UInt32 lock_value[2];
		lock_value[0] = 0;
		lock_value[1] = 0;
		bool locked = ((IOFWCompareAndSwapCommand*)cmd->fCommand)->locked( lock_value );
	
		UInt64 args[7];
		args[0] = (UInt64)status;
		args[1] = (UInt64)cmd->fCommand->getBytesTransferred();
		args[2] = (UInt64)cmd->fCommand->getAckCode();
		args[3] = (UInt64)cmd->fCommand->getResponseCode();
		args[4] = (UInt64)locked;
		args[5] = (UInt64)lock_value[0];
		args[6] = (UInt64)lock_value[1];
#if 0
		int i = 0;
		for( i = 0; i < 7; i++ )
		{
#ifdef __LP64__			
			IOLog( "IOFWUserCompareSwapCommand::asyncCompletion - args[%d] - %llx\n", i, args[i] );
#else
			IOLog( "IOFWUserCompareSwapCommand::asyncCompletion- args[%d] - %llx\n", i, args[i] );
#endif
		}
#endif
		
		IOReturn error = kIOReturnSuccess;
		error = IOFireWireUserClient::sendAsyncResult64( cmd->fAsyncRef, status, args, 7 );
		DebugLogCond ( error, "IOFireWireUserClient::asyncCompareSwapCommandCompletion: sendAsyncResult64 returned error 0x%08x\n", error ) ;


#if 0
		CompareSwapSubmitResult sendResult ;
		
		sendResult.result = status ;
		sendResult.bytesTransferred = cmd->fCommand->getBytesTransferred() ;
		sendResult.lockInfo.didLock	= (Boolean)((IOFWCompareAndSwapCommand*)cmd->fCommand)->locked( (UInt32*) & sendResult.lockInfo.value ) ;
		sendResult.ackCode = cmd->fCommand->getAckCode();
		sendResult.responseCode = cmd->fCommand->getResponseCode();
	
		IOReturn error = IOFireWireUserClient::sendAsyncResult64( cmd->fAsyncRef, status, (io_user_reference_t *)& sendResult, sizeof(sendResult)/sizeof(UInt32) ) ;	// +1 to round up
		DebugLogCond ( error, "IOFireWireUserClient::asyncCompareSwapCommandCompletion: sendAsyncResult64 returned error 0x%08x\n", error ) ;
#endif

	}
}

#pragma mark -
// ============================================================
// IOFWUserAsyncStreamCommand
// ============================================================
bool
IOFWUserAsyncStreamCommand::initWithSubmitParams(
	const CommandSubmitParams*	params,
	const IOFireWireUserClient*			inUserClient)
{
	bool	result = true ;

	result = (0 != IOFWUserCommand::initWithSubmitParams(params, inUserClient));
	
	if (result)
	{
		fMem = IOMemoryDescriptor::withAddressRange( params->newBuffer, params->newBufferSize, kIODirectionOut, fUserClient->getOwningTask() ) ;
		result = (NULL != fMem) ;
	}

	if (result)
	{
		IOReturn error = fMem->prepare() ;
		result = (error == kIOReturnSuccess) ;
	}

	if (!result)
	{
		if (fMem)
		{
			fMem->release() ;
			fMem = NULL;
		}
	}

	return result;
}

IOReturn
IOFWUserAsyncStreamCommand::submit(
	CommandSubmitParams*	params,
	CommandSubmitResult*	outResult)
{
	IOReturn	result		= kIOReturnSuccess;
	Boolean		syncFlag 	= (params->flags & kFWCommandInterfaceSyncExecute) != 0;

	if ( params->staleFlags & kFireWireCommandStale_Buffer )	// IOFWUserAsyncStreamCommand
	{
		if ( fMem )	// whatever happens, we're going to need a new memory descriptor
		{
			fMem->complete() ;
			fMem->release() ;
            fMem =NULL;
		}
	
		if (NULL == (fMem = IOMemoryDescriptor::withAddressRange( params->newBuffer, 
												params->newBufferSize, 
												kIODirectionOut, 
												fUserClient->getOwningTask())) )
		{
			result = kIOReturnNoMemory ;
		}
		else
		{
			result = fMem->prepare() ;
			
			if ( kIOReturnSuccess != result )
			{
				fMem->release() ;
				fMem = NULL ;
			}
		}
	}

	if ( kIOReturnSuccess == result)
	{
		if (fAsyncStreamCommand)
		{
			fAsyncStreamCommand->release() ;
			fAsyncStreamCommand = NULL ;
		}

		if (fAsyncStreamCommand)
		{
			result = ((IOFWAsyncStreamCommand*)fAsyncStreamCommand)->reinit( params->newGeneration,
																			 params->data1,				// channel
																			 params->sync,
																			 params->tag,
																			 fMem, 
																			 params->newBufferSize,
																			 params->maxPacketSpeed,
																			 syncFlag ? NULL : & IOFWUserAsyncStreamCommand::asyncStreamCommandCompletion,
																			 this,
																			 params->newFailOnReset) ;
			DebugLogCond ( result, "IOFWUserAsyncStreamCommand::submit: fCommand->reinit result=%08x\n", result) ;
		}
		else
		{
			IOFireWireController * control = fUserClient->getOwner()->getController();

		    fAsyncStreamCommand = control->createAsyncStreamCommand( params->newGeneration,
																	 params->data1,						// channel
																	 params->sync,
																	 params->tag,
																	 fMem, 
																	 params->newBufferSize,
																	 params->maxPacketSpeed,
																	 syncFlag ? NULL : & IOFWUserAsyncStreamCommand::asyncStreamCommandCompletion,
																	 this,
																	 params->newFailOnReset);
			
			if (!fAsyncStreamCommand)
				result = kIOReturnNoMemory ;
		}
	}

	
	if ( kIOReturnSuccess == result)
	{
		if( params->staleFlags & kFireWireCommandStale_Timeout )
		{
			fAsyncStreamCommand->setTimeout( params->timeoutDuration );
		}

		result = fAsyncStreamCommand->submit() ;
		
		DebugLogCond ( result, "IOFWUserAsyncStreamCommand::submit: fCommand->submit result=%08x\n", result);
	}
						
	if( syncFlag && (outResult != NULL) )
	{
		outResult->result 			= fAsyncStreamCommand->getStatus();
		outResult->responseCode		= 0;

		// mach won't copy any of our result info out on an error, pretend everything is fine
		result = kIOReturnSuccess;
	}	
	
	return result;
}

void
IOFWUserAsyncStreamCommand::free()
{
	if( fAsyncStreamCommand )
	{
		IOReturn	cmdStatus = fAsyncStreamCommand->getStatus();
		if ( cmdStatus == kIOReturnBusy || cmdStatus == kIOFireWirePending )
		{
			DebugLog("cancelling cmd %p\n", fAsyncStreamCommand);
			fAsyncStreamCommand->cancel( kIOReturnAborted );
		}
		
		fAsyncStreamCommand->release() ;
		fAsyncStreamCommand = NULL;
	}
	
	IOFWUserCommand::free();
}

void
IOFWUserAsyncStreamCommand::asyncStreamCommandCompletion(void						*refcon, 
														IOReturn					status, 
														IOFireWireBus				*bus,
														IOFWAsyncStreamCommand		*fwCmd )
{	
	IOFWUserAsyncStreamCommand*	cmd = (IOFWUserAsyncStreamCommand*) refcon ;

	if ( refcon && cmd->fAsyncRef[0] ) 
	{
		io_user_reference_t args[3];
		args[0] = 8;
		
		IOReturn error = kIOReturnSuccess;
		error = IOFireWireUserClient::sendAsyncResult64( cmd->fAsyncRef, status, args, 3 );
		
		DebugLogCond ( error, "IOFWUserCommand::asyncStreamCommandCompletion: sendAsyncResult64 returned error %x\n", error ) ;
	}
}
