bool 
com_ximeta_driver_NDASProtocolTransport::SendSCSICommand (
														  SCSITaskIdentifier 	request,
														  SCSIServiceResponse * serviceResponse,
														  SCSITaskStatus * 		taskStatus 
														  )
{	
	UInt8							commandLength		= 0;
	bool							commandProcessed	= true;
	com_ximeta_driver_NDASSCSICommand	*NDCmd_ptr			= NULL;
	SCSICommand						scsiCommand;
	
	DbgIOLog(DEBUG_MASK_DISK_TRACE, ("Entered. Request %p\n", request));
	
	*serviceResponse	= kSCSIServiceResponse_Request_In_Process;
	*taskStatus			= kSCSITaskStatus_No_Status;
	
	commandLength = GetCommandDescriptorBlockSize ( request );
	
#if 0
	SCSICommandDescriptorBlock		cdb					= { 0 };
	
	DbgIOLog(DEBUG_MASK_DISK_WARNING, ("Data Transfer Request %lld\n", GetRequestedDataTransferCount( request )));
	
	GetCommandDescriptorBlock ( request, &cdb );
	
	if ( commandLength == kSCSICDBSize_6Byte )
	{
		
		DbgIOLog(DEBUG_MASK_DISK_WARNING, ("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 )
	{
		
		DbgIOLog(DEBUG_MASK_DISK_WARNING, ("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 )
	{
		
		DbgIOLog(DEBUG_MASK_DISK_WARNING, ("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
	
	request->retain();

	if ( isInactive ())
	{
		DbgIOLog(DEBUG_MASK_DISK_WARNING, ("Device is inactive.\n"));
		
		// device is disconnected - we can not service command request
		*serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
		commandProcessed = false;
		
		goto exit;
	}
	
	// Check Workable.
	if (!fProvider 
		|| !fProvider->isWorkable()) {
		
		DbgIOLog(DEBUG_MASK_DISK_WARNING, ("Device is not workable.\n"));

		// device is disconnected - we can not service command request
		*serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
		commandProcessed = false;
		
		goto exit;		
	}
	
	// Check Busy.
//	if (fProvider->isBusy() )
	if (fBusy)
	{
		DbgIOLog(DEBUG_MASK_DISK_INFO, ("Busy!!!\n"));

		*serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
		commandProcessed = false;
		goto exit;
	}
	
	// Allocate Command.
	/*
	NDCmd_ptr = OSDynamicCast(com_ximeta_driver_NDASSCSICommand, 
							  OSMetaClass::allocClassWithName(NDAS_SCSI_COMMAND_CLASS));
    
    if(NDCmd_ptr == NULL || !NDCmd_ptr->init()) {
        DbgIOLog(DEBUG_MASK_DISK_ERROR, ("failed to alloc command class\n"));
		
		if (NDCmd_ptr) {
			NDCmd_ptr->release();
		}
		
		*serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
		commandProcessed = true;
		goto exit;
    }
	*/
	NDCmd_ptr = (com_ximeta_driver_NDASSCSICommand*)fCommandPool->getCommand(false);
	
	if (NDCmd_ptr == NULL) {

		DbgIOLog(DEBUG_MASK_DISK_ERROR, ("No Commands.\n"));

		*serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
		commandProcessed = true;
		goto exit;
	}
	
	NDCmd_ptr->init();
	
	memset(&scsiCommand, 0, sizeof(SCSICommand));
	
	scsiCommand.scsiTask = request;
//	scsiCommand.targetNo = fProvider->targetNo();
	scsiCommand.LogicalUnitNumber = GetLogicalUnitNumber( request );
	GetCommandDescriptorBlock ( request, &scsiCommand.cdb );
	scsiCommand.cdbSize = commandLength;
	scsiCommand.MemoryDescriptor_ptr = GetDataBuffer( request );
	scsiCommand.BufferOffset = GetDataBufferOffset( request );
	scsiCommand.RequestedDataTransferCount = GetRequestedDataTransferCount( request );
	scsiCommand.DataTransferDirection = GetDataTransferDirection( request );
//	scsiCommand.BlockSize = fProvider->blocksize();
		
	NDCmd_ptr->setCommand(&scsiCommand);
		
	//	CriticalCommandSubmission(NDCmd_ptr, &commandProcessed);
	
	fBusy = true;
	
	fCommandGate->runAction ( CriticalCommandSubmissionStatic, NDCmd_ptr);
	
	
	//fXiCommandGate->runCommand((void *)kNDASProtocolTransportRequestSendCommand, NDCmd_ptr, &commandProcessed);
	
exit:
		
	if ( kSCSIServiceResponse_Request_In_Process != *serviceResponse ) {
		
		DbgIOLog(DEBUG_MASK_DISK_INFO, ("ERROR Service Response = %x, task = %p\n", *serviceResponse, request));
		
		request->release();
		
		if(NDCmd_ptr) {
			NDCmd_ptr->release();
		}
	}
	
	DbgIOLog(DEBUG_MASK_DISK_INFO,("exit, Service Response = %x, task = %p\n", *serviceResponse, request));
	
	return commandProcessed;
	
}
SCSIServiceResponse
com_apple_dts_SCSIEmulatorAdapter::ProcessParallelTask ( SCSIParallelTaskIdentifier parallelRequest )
{
	// Not all of these may be required.  Unused ones are commented out to avoid compiler warnings
	SCSITargetIdentifier		targetID				= GetTargetIdentifier(parallelRequest);
//	SCSITaskIdentifier			task					= GetSCSITaskIdentifier(parallelRequest);
//	SCSITaggedTaskIdentifier	taggedTask				= GetTaggedTaskIdentifier(parallelRequest);
	SCSILogicalUnitNumber		lun						= GetLogicalUnitNumber(parallelRequest);
//	SCSITaskAttribute			taskAttribute			= GetTaskAttribute(parallelRequest);
	
	UInt8						transferDir				= GetDataTransferDirection(parallelRequest);
	UInt64						transferSize			= GetRequestedDataTransferCount(parallelRequest);
	IOMemoryDescriptor *		transferMemDesc			= GetDataBuffer(parallelRequest);
//	UInt64						transferMemDescOffset   = GetDataBufferOffset(parallelRequest);

	// Get the CDB
	UInt8						cdbLength				= GetCommandDescriptorBlockSize(parallelRequest);
	SCSICommandDescriptorBlock  cdbData;
	
	SCSITaskStatus 				scsiStatus 				= kSCSITaskStatus_GOOD;
	UInt64 						dataLen 				= 0;

	UInt8						senseBuffer[kSenseBufferLen];
	UInt64						senseBufferLen 			= sizeof(senseBuffer);

	com_apple_dts_SCSIEmulator *emulator = (com_apple_dts_SCSIEmulator *)mTargetsArray->getObject(targetID);

	// Fail if we don't have a SCSI emulator backing this target
	if (!emulator) {
		IOLog("ProcessParallelTask: ABORT - !emulator for targetID = %ud\n", targetID);
		goto failure_exit;
	}

	// Fail if we're supposed to transfer data and don't have a data buffer
	if (!transferMemDesc && (transferDir != kSCSIDataTransfer_NoDataTransfer)) {
		IOLog("ProcessParallelTask: ABORT - !transferMemDesc && (transferDir != kSCSIDataTransfer_NoDataTransfer) - %p and %d\n", transferMemDesc, transferDir);
		goto failure_exit;
	}

	// Fail if we don't have a large enough CDB buffer set aside
	if (cdbLength > sizeof(cdbData)) {
		IOLog("ProcessParallelTask: ABORT - cdbLength > sizeof(cdbData) - %d vs. %d\n", cdbLength, sizeof(cdbData));
		goto failure_exit;
	}

	if (!GetCommandDescriptorBlock(parallelRequest, &cdbData)) {
		IOLog("ProcessParallelTask: ABORT - !GetCommandDescriptorBlock(parallelRequest, &cdbData)\n");
		goto failure_exit;
	}

	if (transferMemDesc && (transferDir != kSCSIDataTransfer_NoDataTransfer)) {
#if 0
		// This block isn't necessary as memory descriptors passed in are always autoprepared for us.
		// Remember: Any memory descriptors allocated and used internally should be prepared before sending
		// or receiving data to/from real hardware
		IOReturn res = transferMemDesc->prepare();
		if (res != kIOReturnSuccess) {
			goto failure_exit;
		}
#endif

		// We are guaranteed that the memory descriptor will always be sized large enough by
		// by SAM/STUC to hold the transfer size requested
		dataLen = transferSize;
	}

	// This is where the "real" work should get done by your hardware.  The individual parameters
	// are being sent instead of just the task reference as the task is opaque by design and
	// the getter/setter methods are protected and available within this class, but not within
	// the emulator itself.
	emulator->sendCommand(cdbData, cdbLength, transferMemDesc, &dataLen, lun, &scsiStatus, senseBuffer, &senseBufferLen);

	// Real hardware should be doing the task processing internally and providing responses
	// via an interrupt mechanism.  IOSCSIParallelInterfaceController expects this and you
	// should always do your task completions from the workloop thread.
	CompleteTaskOnWorkloopThread(parallelRequest, true, scsiStatus, dataLen, senseBuffer, senseBufferLen);
	return kSCSIServiceResponse_Request_In_Process;

failure_exit:
	CompleteTaskOnWorkloopThread(parallelRequest, false, scsiStatus, dataLen, senseBuffer, senseBufferLen);
	return kSCSIServiceResponse_Request_In_Process;
}