IOATAPIProtocolTransport.cpp   [plain text]


/*
 * Copyright (c) 2000-2008 Apple Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * 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 2.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.opensource.apple.com/apsl/ 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, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
// BSD includes
#include <sys/sysctl.h>

// IOKit includes
#include <libkern/OSAtomic.h>
#include <IOKit/IOMemoryDescriptor.h>
#include <IOKit/IOCommandGate.h>
#include <IOKit/IOWorkLoop.h>
#include <IOKit/IOKitKeys.h>
#include <IOKit/ata/IOATATypes.h>
#include <IOKit/ata/IOATADevConfig.h>
#include <IOKit/ata/IOATABusInfo.h>
#include <IOKit/ata/IOATACommand.h>
#include <IOKit/scsi/SCSICommandOperationCodes.h>
#include <IOKit/scsi/SCSITask.h>
#include <IOKit/scsi/SCSITaskDefinition.h>			// Remove me when API is available for IsAutosenseRequested()

#include "IOATAPIProtocolTransport.h"
#include "IOATAPIProtocolTransportTimeStamps.h"
#include "IOATAPIProtocolTransportDebugging.h"

#include <IOKit/storage/ata/IOATAFamilyPriv.h>
#include <IOKit/IOTimeStamp.h>


#define ATAPI_PROTOCOL_TRANSPORT_DEBUGGING_LEVEL 0

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

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

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

#define DEBUG_UNUSED( X )		( void )( X )


// Timeout values used by the ATAPI Driver
enum 
{
	kNoTimeout			= 0,					// Constant to indicate no timeout
	k1SecondTimeout		= 1000,					// 1000 mS = 1 Sec
	k10SecondTimeout	= 10 * k1SecondTimeout,	
	k30SecondTimeout	= 3 * k10SecondTimeout,
	k45SecondTimeout	= 45000,
	k100Milliseconds	= 100
};

enum
{
	kMaxATAPIPacketSize					= 16,					// Max ATAPI packet size
	kATAPICommandLength 				= 12,					// ATAPI command length
	kATAPIIdentifyPacketDeviceDataSize	= 512					// 512 byte identify data
};

enum
{
	kATAPICheckConditionBit				= 0,
	kATAPIDeviceBusyBit					= 8
};

enum
{
	kATAPICheckConditionMask			= ( 1 << kATAPICheckConditionBit ),
	kATAPIDeviceBusyMask				= ( 1 << kATAPIDeviceBusyBit )
};

// Configuration state machine
enum
{
	kPIOTransferModeSetup	= 1,
	kPIOTransferModeDone	= 2,
	kDMATransferModeDone	= 3
};

struct ATAPIConfigData
{
	IOATAPIProtocolTransport *	self;
	UInt32						state;
	bool						done;
};
typedef struct ATAPIConfigData ATAPIConfigData;

#define kIOATAPICommandPoolSize			1

enum
{
	kATAPICommandBusyBit			= 0,
	kATAPIRequestSenseNeededBit		= 1
};

enum
{
	kATAPICommandBusyMask			= ( 1 << kATAPICommandBusyBit ),
	kATAPIRequestSenseNeededMask	= ( 1 << kATAPIRequestSenseNeededBit )
};

enum
{
	kODDMediaNotifyValue0	= 0,
	kODDMediaNotifyValue1	= 1
};

#define	kATAPI5SecondsInNanoseconds		( 5LL * 1000LL * 1000LL * 1000LL )

#define fSemaphore			reserved->fSemaphore
#define fMediaNotifyValue	reserved->fMediaNotifyValue

#define super IOSCSIProtocolServices
OSDefineMetaClassAndStructors ( IOATAPIProtocolTransport, IOSCSIProtocolServices );


//-----------------------------------------------------------------------------
//      Class
//-----------------------------------------------------------------------------

class ATAPITransportGlobals
{

public:

	// Constructor
	ATAPITransportGlobals ( void );

	// Destructor
	virtual ~ATAPITransportGlobals ( void );

};


//-----------------------------------------------------------------------------
//      Prototypes
//-----------------------------------------------------------------------------

static inline void
RecordATAPITimeStamp (
	unsigned int code,
	unsigned int a = 0, unsigned int b = 0,
	unsigned int c = 0, unsigned int d = 0 );

static int
ATAPITransportSysctl (
	struct sysctl_oid * oidp,
	void *				arg1,
	int 				arg2,
	struct sysctl_req * req );


//-----------------------------------------------------------------------------
//  Globals
//-----------------------------------------------------------------------------
static ATAPITransportGlobals	gATAPIGlobals;
UInt32							gATAPIDebugFlags = 0;

SYSCTL_PROC ( _debug, OID_AUTO, ATAPITransport, CTLFLAG_RW, 0, 0, ATAPITransportSysctl, "ATAPITransport", "ATAPI Transport debug interface" );


#pragma mark Public Methods


//-----------------------------------------------------------------------------
//  ATAPITransportSysctl - Sysctl handler.                      [PRIVATE]
//-----------------------------------------------------------------------------

static int
ATAPITransportSysctl ( struct sysctl_oid * oidp, void * arg1, int arg2, struct sysctl_req * req )
{

    int             	error = 0;
    ATAPISysctlArgs    	sysctlArgs;

    DEBUG_UNUSED ( oidp );
    DEBUG_UNUSED ( arg1 );
    DEBUG_UNUSED ( arg2 );

    ERROR_LOG  ( ( "+ATAPITransportSysctl: gATAPIDebugFlags = 0x%08X\n", ( unsigned int ) gATAPIDebugFlags ) );

    error = SYSCTL_IN ( req, &sysctlArgs, sizeof ( sysctlArgs ) );
    if ( ( error == 0 ) && ( sysctlArgs.type == kATAPITypeDebug ) )
    {

        if ( sysctlArgs.operation == kATAPIOperationGetFlags )
        {

            sysctlArgs.debugFlags = gATAPIDebugFlags;
            error = SYSCTL_OUT ( req, &sysctlArgs, sizeof ( sysctlArgs ) );

        }

        else if ( sysctlArgs.operation == kATAPIOperationSetFlags )
        {
            gATAPIDebugFlags = sysctlArgs.debugFlags;
        }

    }

    STATUS_LOG ( ( "-ATAPITransportSysctl: gATAPIDebugFlags = 0x%08X\n", ( unsigned int ) gATAPIDebugFlags ) );

    return error;

}


//-----------------------------------------------------------------------------
//  Default Constructor
//-----------------------------------------------------------------------------

ATAPITransportGlobals::ATAPITransportGlobals ( void )
{

    int     debugFlags;
   
    STATUS_LOG ( ( "+ATAPITransportGlobals::ATAPITransportGlobals\n" ) );
   
    if ( PE_parse_boot_argn ( "ATAPIdisk", &debugFlags, sizeof ( debugFlags ) ) )
    {

        gATAPIDebugFlags = debugFlags;

    }

    // Register our sysctl interface
    sysctl_register_oid ( &sysctl__debug_ATAPITransport );

    STATUS_LOG ( ( "-ATAPITransportGlobals::ATAPITransportGlobals\n" ) );

}


//-----------------------------------------------------------------------------
//  Destructor
//-----------------------------------------------------------------------------

ATAPITransportGlobals::~ATAPITransportGlobals ( void )
{

    STATUS_LOG ( ( "+~ATAPITransportGlobals::ATAPITransportGlobals\n" ) );

    // Unregister our sysctl interface
    sysctl_unregister_oid ( &sysctl__debug_ATAPITransport );

    STATUS_LOG ( ( "-~ATAPITransportGlobals::ATAPITransportGlobals\n" ) );

}


//--------------------------------------------------------------------------------------
//	init - Initialization
//--------------------------------------------------------------------------------------

bool
IOATAPIProtocolTransport::init ( OSDictionary * propTable )
{
	
	STATUS_LOG ( ( "IOATAPIProtocolTransport::init entering\n" ) );
		
	// Run this by our superclass
	if ( super::init ( propTable ) == false )
	{

		STATUS_LOG ( ( "IOATAPIProtocolTransport::init superclass init returned false\n" ) );
		return false;
	
	}
		
	STATUS_LOG ( ( "IOATAPIProtocolTransport::init returning true\n" ) );
	
	return true;
	
}


//--------------------------------------------------------------------------------------
//	start -	Start our services
//--------------------------------------------------------------------------------------

bool
IOATAPIProtocolTransport::start ( IOService * provider )
{
	
	IOWorkLoop *	workLoop			= NULL;
	OSDictionary *	dict				= NULL;
	IOService *		powerProvider		= NULL;
	OSNumber *		mediaNotifyValue	= NULL;
	
	STATUS_LOG ( ( "IOATAPIProtocolTransport::start called\n" ) );
	
	fATAUnitID				= kATAInvalidDeviceID;
	fATADeviceType			= kUnknownATADeviceType;
	fPhysicallyConnected 	= true;
	
	reserved = IONew ( ExpansionData, 1 );
	if ( reserved == NULL )
	{
		return false;
	}
	bzero ( reserved, sizeof ( ExpansionData ) );
	
	dict = OSDynamicCast ( OSDictionary, getProperty ( kIOPropertyATAPIMassStorageCharacteristics ) );
	if ( dict != NULL )
	{
		
		OSString *	string = NULL;
		
		string = OSDynamicCast ( OSString, dict->getObject ( kATAVendorPropertyKey ) );
		if ( string != NULL )
		{
			
			const char * cString1 = NULL;
			const char * cString2 = NULL;
			
			cString1 = ( ( OSString * ) provider->getProperty ( kATAVendorPropertyKey ) )->getCStringNoCopy ( );
			cString2 = string->getCStringNoCopy ( );
			
			STATUS_LOG ( ( "device model = %s\n", cString1 ) );
			STATUS_LOG ( ( "ATAPI Device Characteristics device model = %s\n", cString2 ) );
			
			if ( strncmp ( cString1, cString2, string->getLength ( ) ) )
			{
				
				// Not a match to what is in the dictionary, so this workaround driver
				// should not be loaded. Short circuit out and let another driver attempt
				// to load.
				return false;
				
			}
			
		}
		
		else
		{
			STATUS_LOG ( ( "ATAPI Mass Storage dictionary has no device model string\n" ) );
		}
		
	}
	
	else
	{
		STATUS_LOG ( ( "No ATAPI Mass Storage dictionary\n" ) );
	}
	
	mediaNotifyValue = OSDynamicCast ( OSNumber, getProperty ( "media-notify", gIOServicePlane ) );
	
	if ( mediaNotifyValue != NULL )
	{
		fMediaNotifyValue = mediaNotifyValue->unsigned32BitValue ( );
	}
	
	else
	{
		fMediaNotifyValue = kODDMediaNotifyValue0;
	}
	
	// First call start() in our superclass
	if ( super::start ( provider ) == false )
		return false;
	
	// Cache our provider
	fATADevice = OSDynamicCast ( IOATADevice, provider );
	if ( fATADevice == NULL )
	{
		
		ERROR_LOG ( ( "Error in dynamic cast\n" ) );
		// Error in the dynamic cast, so get out
		return false;
		
	}
	
	// Find out if the device type is ATAPI
	if ( fATADevice->getDeviceType ( ) != ReportATAPIDeviceType ( ) )
	{
		
		ERROR_LOG ( ( "exiting, not an ATAPI device.\n" ) );
		return false;
		
	}
	
	// Open the thing below us
	if ( fATADevice->open ( this ) == false )
	{
		
		ERROR_LOG ( ( "device wouldn't open\n" ) );
		// It wouldn't open, so bail
		return false;
		
	}
	
	fATAUnitID 		= fATADevice->getUnitID ( );
	fATADeviceType 	= fATADevice->getDeviceType ( );
	
	STATUS_LOG ( ( "unit ID is %d\n", ( UInt8 ) fATAUnitID ) );
	STATUS_LOG ( ( "deviceType is %d\n", ( UInt8 ) fATADeviceType ) );

	RecordATAPITimeStamp (
		ATAPI_TRACE ( kATADeviceInfo ),
		( unsigned int ) ( uintptr_t ) this,
		( unsigned int ) fATAUnitID,
		( unsigned int ) fATADeviceType );
	
	bzero ( fDeviceIdentifyData, kATAPIIdentifyPacketDeviceDataSize );
	
	fDeviceIdentifyBuffer = IOMemoryDescriptor::withAddress ( ( void * ) fDeviceIdentifyData,
																kATAPIIdentifyPacketDeviceDataSize,
																kIODirectionIn );
	
	if ( fDeviceIdentifyBuffer == NULL )
	{
		
		ERROR_LOG ( ( "fDeviceIdentifyBuffer == NULL.\n" ) );
		goto CLOSE_DEVICE_ERROR;
		
	}
	
	fCommandGate = GetCommandGate ( );
	workLoop = getWorkLoop ( );
	
	fCommandPool = IOCommandPool::withWorkLoop ( workLoop );
	if ( fCommandPool == NULL )
	{
		
		ERROR_LOG ( ( "fCommandPool == NULL.\n" ) );
		goto RELEASE_IDENTIFY_DEVICE_BUFFER_ERROR;
		
	}
	
	fPollingThread = thread_call_allocate (
						( thread_call_func_t ) IOATAPIProtocolTransport::sPollStatusRegister,
						( thread_call_param_t ) this );
		
	if ( fPollingThread == NULL )
	{
		
		ERROR_LOG ( ( "fPollingThread allocation failed.\n" ) );
		goto RELEASE_COMMAND_POOL_ERROR;
		
	}
	
	// Pre-allocate some command objects	
	AllocateATACommandObjects ( );
	
	// Inspect the provider
	if ( InspectDevice ( fATADevice ) == false )
	{
		
		ERROR_LOG ( ( "InspectDevice returned false.\n" ) );
		goto DEALLOCATE_COMMANDS_ERROR;
		
	}
	
	// Initialize the power provider to default
	powerProvider = provider;
	powerProvider->retain ( );
	
	// Look to see if we are the slave device and there is a master
	// device on the bus.
	if ( fATAUnitID == kATADevice1DeviceID )
	{
		
		IOService *		obj;
		OSIterator *	iter;
		OSNumber *		deviceNumber;
		
		STATUS_LOG ( ( "We are the slave, find a master.\n" ) );
		
		// We are the slave. Find a master.
		obj = provider->getProvider ( );
		
		iter = obj->getChildIterator ( gIOServicePlane );
		if ( iter != NULL )
		{
			
			STATUS_LOG ( ( "Got an iterator.\n" ) );

			while ( ( obj = ( IOService * ) iter->getNextObject ( ) ) != NULL )
			{
				
				STATUS_LOG ( ( "Looping over objects.\n" ) );

				if ( obj == provider )
					continue;

				STATUS_LOG ( ( "Check the IOUnit property.\n" ) );
				
				deviceNumber = OSDynamicCast ( OSNumber, obj->getProperty ( "IOUnit" ) );
				if ( deviceNumber != NULL )
				{
					
					STATUS_LOG ( ( "Found the IOUnit property.\n" ) );

					if ( deviceNumber->unsigned8BitValue ( ) == kATADevice0DeviceID )
					{
						
						IOService *			possibleProvider 	= NULL;
						IOReturn			status 				= kIOReturnSuccess;
						
						// Wait upto 5 seconds for matching to finish on master device.
						status = obj->waitQuiet ( kATAPI5SecondsInNanoseconds );
						if ( status == kIOReturnTimeout )
						{
							break;
						}
						
						// Find this object's child to get the item which is the master.
						possibleProvider = ( IOService * ) obj->getChildEntry ( gIOServicePlane );
						if ( possibleProvider != NULL )
						{
							
							STATUS_LOG ( ( "Found the master.\n" ) );
							
							// This is our new power provider.
							powerProvider->release ( );
							powerProvider = possibleProvider;
							powerProvider->retain ( );
							break;
							
						}
						
					}
					
				}
				
			}
			
			iter->release ( );
			
		}
		
	}
	
	InitializePowerManagement ( powerProvider );
	
	powerProvider->release ( );
	
	STATUS_LOG ( ( "IOATAPIProtocolTransport::start complete\n" ) );
	
	dict = OSDictionary::withCapacity ( 1 );
	if ( dict != NULL )
	{
	
		// Copy some properties into the dictionary.
		dict->setObject ( kATAUnitNumberKey, fATADevice->getProperty ( kATAUnitNumberKey ) );
		setProperty ( kIOPropertyProtocolCharacteristicsKey, dict );
		dict->release ( );
		
	}
	
	registerService ( );
	return true;
	
	
DEALLOCATE_COMMANDS_ERROR:
	
	DeallocateATACommandObjects ( );
	
	
RELEASE_COMMAND_POOL_ERROR:
	
	if ( fCommandPool != NULL )
		fCommandPool->release ( );
	
	
RELEASE_IDENTIFY_DEVICE_BUFFER_ERROR:
	
	if ( fDeviceIdentifyBuffer != NULL )
		fDeviceIdentifyBuffer->release ( );
	
	
CLOSE_DEVICE_ERROR:
	
	fATADevice->close ( this );
	return false;
	
}


//--------------------------------------------------------------------------------------
//	stop - Stop our services
//--------------------------------------------------------------------------------------

void
IOATAPIProtocolTransport::stop ( IOService * provider )
{

	STATUS_LOG ( ( "IOATAPIProtocolTransport::stop called\n" ) );
	
	// Call super's stop
	super::stop ( provider );
	
}


//--------------------------------------------------------------------------------------
//	free -	Called to free any resources we allocated.
//--------------------------------------------------------------------------------------

void
IOATAPIProtocolTransport::free ( void )
{
	
	if ( fCommandPool != NULL )
	{
		
		fCommandPool->release ( );
		fCommandPool = NULL;
		
	}
	
	if ( reserved != NULL )
	{
		IODelete ( reserved, ExpansionData, 1 );
		reserved = NULL;
	}
	
	if ( fPollingThread != NULL )
	{
		
		thread_call_cancel ( fPollingThread );
		thread_call_free ( fPollingThread );
		fPollingThread = NULL;
		
	}
	
	super::free ( );
	
}


#pragma mark Protected Methods


//--------------------------------------------------------------------------------------
//	ReportATAPIDeviceType - Report the type of the device (ATA vs. ATAPI).
//--------------------------------------------------------------------------------------

ataDeviceType
IOATAPIProtocolTransport::ReportATAPIDeviceType ( void ) const
{
	
	return kATAPIDeviceType;
	
}


//--------------------------------------------------------------------------------------
//	SendSCSICommand	- Sends a SCSI Command to the provider bus
//--------------------------------------------------------------------------------------

bool
IOATAPIProtocolTransport::SendSCSICommand ( SCSITaskIdentifier request,
											SCSIServiceResponse * serviceResponse,
											SCSITaskStatus * taskStatus )
{
	
	SCSICommandDescriptorBlock 		cdb;
	UInt16 							commandLength		= 0;
	IOATACommand *					cmd					= NULL;
	ATAPIClientData *				clientData			= NULL;
	UInt16							atapiCommandLength	= kATAPICommandLength;
	UInt32							flags				= 0;
	UInt64							requestCount 		= 0;
	UInt32							timeoutDuration		= 0;
	bool							shouldUseDMA		= true;
	
	STATUS_LOG ( ( "IOATAPIProtocolTransport::SendSCSICommand called\n" ) );
    
	if ( OSBitOrAtomic ( kATAPICommandBusyMask, &fSemaphore ) & kATAPICommandBusyMask )
	{
		STATUS_LOG ( ( "Command in use, returning false\n" ) );

		RecordATAPITimeStamp (
			ATAPI_TRACE ( kATASendSCSICommandFailed ),
			( unsigned int ) ( uintptr_t ) this,
			( unsigned int ) fATAUnitID, 
			( unsigned int ) ( uintptr_t ) request );

		return false;
	}
	
	if ( fSemaphore & kATAPIRequestSenseNeededMask )
	{
		
		STATUS_LOG ( ( "kATAPIRequestSenseNeededMask set\n" ) );
		
		if ( GetTaskExecutionMode ( request ) != kSCSITaskMode_Autosense )
		{
			
			STATUS_LOG ( ( "Not an autosense command, returning false.\n" ) );
			OSBitAndAtomic ( ~kATAPICommandBusyMask, &fSemaphore );

			RecordATAPITimeStamp (
				ATAPI_TRACE ( kATASendSCSICommandFailed ),
				( unsigned int ) ( uintptr_t ) this,
				( unsigned int ) fATAUnitID,
				( unsigned int ) ( uintptr_t ) request );

			return false;
			
		}
		
		STATUS_LOG ( ( "Clearing kATAPIRequestSenseNeededMask.\n" ) );
		OSBitAndAtomic ( ~kATAPIRequestSenseNeededMask, &fSemaphore );
		
	}
	
	// get command and context objects
	cmd = GetATACommandObject ( );
	
	clientData 			= ( ATAPIClientData * ) cmd->refCon;	
	*serviceResponse 	= kSCSIServiceResponse_Request_In_Process;
	*taskStatus			= kSCSITaskStatus_No_Status;

	RecordATAPITimeStamp (
		ATAPI_TRACE ( kATASendSCSICommand ),
		( unsigned int ) ( uintptr_t ) this,
		( unsigned int ) fATAUnitID,
		( unsigned int ) ( uintptr_t ) cmd,
		( unsigned int ) ( uintptr_t ) request );
	
	if ( fPhysicallyConnected == false )
	{
		
		// device is disconnected - we can not service command request
		*serviceResponse = kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
		ReturnATACommandObject ( cmd );
		OSBitAndAtomic ( ~kATAPICommandBusyMask, &fSemaphore );

		RecordATAPITimeStamp (
			ATAPI_TRACE ( kATASendSCSICommandFailed ),
			( unsigned int ) ( uintptr_t ) this,
			( unsigned int ) fATAUnitID,
			( unsigned int ) ( uintptr_t ) request );

		return false;
		
	}
	
	GetCommandDescriptorBlock ( request, &cdb );
	commandLength = GetCommandDescriptorBlockSize ( request );
	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] ) );
		
	}
	
	else if ( commandLength == kSCSICDBSize_16Byte )
	{
		
		STATUS_LOG ( ( "cdb = %02x:%02x:%02x:%02x:%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], cdb[12], cdb[13], cdb[14], cdb[15] ) );
		
	}
	
	cmd->zeroCommand ( );
	
	// Start filling in the command
	cmd->setUnit ( fATAUnitID );
	cmd->setBuffer ( GetDataBuffer ( request ) );
	cmd->setPosition ( GetDataBufferOffset ( request ) );
	cmd->setByteCount ( GetRequestedDataTransferCount ( request ) );
	cmd->setCommand ( kPACKET );
	
	timeoutDuration = GetTimeoutDuration ( request );
	if ( timeoutDuration == 0 )
	{
		
		// Find out what the timeout duration is. Since timeouts of zero requested from
		// the layer above us mean the maximum timeout possible and ATA has no concept of
		// infinite timeouts on commands, set it to the max possible.
		timeoutDuration = 0xFFFFFFFF;
		
	}
	
	cmd->setTimeoutMS ( timeoutDuration );
	
	// Configure the flags for this command
	flags = mATAFlagProtocolATAPI | mATAFlagUseConfigSpeed /* | mATAFlagLEDEnable */;
	flags = flags | ( GetDataTransferDirection ( request ) == kSCSIDataTransfer_FromTargetToInitiator ? mATAFlagIORead : 0 );
	flags = flags | ( GetDataTransferDirection ( request ) == kSCSIDataTransfer_FromInitiatorToTarget ? mATAFlagIOWrite : 0 );
	
	requestCount = GetRequestedDataTransferCount ( request );
	
	// Check if this is an operation we should even use DMA on. This is really ugly, but it gains us some
	// performance on reads and writes.
	if ( ( cdb[0] == kSCSICmd_READ_6 )  || ( cdb[0] == kSCSICmd_READ_10 ) 	  || ( cdb[0] == kSCSICmd_READ_12 ) || 
		 ( cdb[0] == kSCSICmd_READ_CD ) || ( cdb[0] == kSCSICmd_READ_CD_MSF ) || ( cdb[0] == kSCSICmd_WRITE_AND_VERIFY_10 ) ||
		 ( cdb[0] == kSCSICmd_WRITE_6 ) || ( cdb[0] == kSCSICmd_WRITE_10 ) 	  || ( cdb[0] == kSCSICmd_WRITE_12 ) )
	{
		shouldUseDMA = true;
	}
	
	else
	{
		shouldUseDMA = false;
	}
	
	if ( ( GetDataTransferDirection ( request ) != kSCSIDataTransfer_NoDataTransfer ) &&
		 ( ( fUltraDMAMode | fDMAMode ) != 0 ) && shouldUseDMA )
	{
		
		UInt8	features = mATAPIuseDMA;
		
		flags = flags | mATAFlagUseDMA;
		
		// Set the features register
		cmd->setFeatures ( features );
		
	}
	
	cmd->setFlags ( flags );
	
	// Set the cylinder registers
	if ( GetRequestedDataTransferCount ( request ) != 0 )
	{
		
		UInt64		requestCount 	= GetRequestedDataTransferCount ( request );
		
		if ( requestCount >= 0x10000 )
		{
			
			// Cap the amount of PIO data that can be transferred in one interrupt
			// so we don't try to hog the cpu while doing PIO transfers.
			
			// Look and see if the caller is asking for 2352 byte transfers (CDDA)
			// if so, then use a multiple of 2352 bytes for the chunk size
			if ( ( requestCount % 2352 ) == 0 )
			{
				
				requestCount = ( 0xFFFF / 2352 ) * 2352;
				
			}
			
			// Caller is asking for non-CDDA data reads, so we use 62k to get the largest
			// size transfer less than 64k we possibly can which uses even block multiples
			else
			{
				
				requestCount = 0xF800;
			
			}
			
		}
		
		UInt8		requestHi		= ( requestCount & 0xFF00 ) >> 8;
		UInt8		requestLo		= requestCount & 0x00FF;
		
		cmd->setCylHi ( requestHi );
		cmd->setCylLo ( requestLo );
		
	}
	
	cmd->setOpcode ( kATAPIFnExecIO );
	// set the device head to the correct unit
	cmd->setDevice_Head ( fATAUnitID << 4 );
	cmd->setRegMask ( ( ataRegMask ) ( mATAErrFeaturesValid | mATAStatusCmdValid ) );
	
	IOReturn theErr = cmd->setPacketCommand ( atapiCommandLength, ( UInt8 * ) cdb );
	if ( theErr != kATANoErr )
	{
		
		STATUS_LOG ( ( "IOATAPIProtocolTransport::SendSCSICommand setPacketCommand returned error = %ld\n", theErr ) );
		
	}
		
	// Setup our context
	clientData->self 		= this;
	clientData->scsiTask 	= request;
	
	cmd->setCallbackPtr ( &sSCSITaskCallbackProc );
		
	fATADevice->executeCommand ( cmd );
	
	return true;
	
}


//--------------------------------------------------------------------------------------
//	SCSITaskCallbackFunction - virtual callback routine which calls CompleteSCSITask
//--------------------------------------------------------------------------------------

void
IOATAPIProtocolTransport::SCSITaskCallbackFunction ( IOATACommand * cmd,
													 SCSITaskIdentifier scsiTask )
{
	
	ATAPIClientData *	clientData	= NULL;
	IOReturn			result;
	UInt64				bytesTransferred;
	
	STATUS_LOG ( ( "IOATAPIProtocolTransport::SCSITaskCallbackFunction entering\n" ) );	
	
	clientData = ( ATAPIClientData * ) cmd->refCon;
	
	result 				= cmd->getResult ( );
	bytesTransferred	= cmd->getActualTransfer ( );
	
	ReturnATACommandObject ( cmd );
	
	switch ( result )
	{
		
		case kATANoErr:
			{
				
				STATUS_LOG ( ( "IOATAPIProtocolTransport::SCSITaskCallbackFunction result = noErr\n" ) );	
				SetRealizedDataTransferCount ( scsiTask, bytesTransferred );
				CompleteSCSITask ( 	scsiTask,
									kSCSIServiceResponse_TASK_COMPLETE,
									kSCSITaskStatus_GOOD );
				
			}
			break;
		
		case kATAErrDevBusy:
		case kATATimeoutErr:
			{
				
				SCSITaskStatus		taskStatus = kSCSITaskStatus_No_Status;
				
				if ( result == kATATimeoutErr )
					taskStatus = kSCSITaskStatus_TaskTimeoutOccurred;
				
				else if ( result == kATAErrDevBusy )
					taskStatus = kSCSITaskStatus_DeviceNotResponding;
				
				// Reset the device because the device is hung
				clientData->self->ResetATAPIDevice ( );
				SetRealizedDataTransferCount ( scsiTask, bytesTransferred );
				CompleteSCSITask ( 	scsiTask,
									kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE,
									taskStatus );
				
				// Since we reset the device, message the upper layer to check its configuration
				// and do anything it needs to do
				SendNotification_VerifyDeviceState ( );
				
				STATUS_LOG ( ( "IOATAPIProtocolTransport::SCSITaskCallbackFunction result = %ld.\n", result ) );
				
			}
			break;
			
		case kATADeviceError:
			{
				
				// CHK bit is set, so the device indicates CheckCondition
				SetRealizedDataTransferCount ( scsiTask, bytesTransferred );
				
				SCSITask *	request = OSDynamicCast ( SCSITask, scsiTask );
				
				if ( request->IsAutosenseRequested ( ) == true )
				{
					OSBitOrAtomic ( kATAPIRequestSenseNeededMask, &fSemaphore );
				}
				
				CompleteSCSITask ( 	scsiTask,
									kSCSIServiceResponse_TASK_COMPLETE,
									kSCSITaskStatus_CHECK_CONDITION );
				
				STATUS_LOG ( ( "IOATAPIProtocolTransport::SCSITaskCallbackFunction result = %ld.\n", result ) );
				
			}
			break;
			
		case kATAModeNotSupported:
		case kATADevIntNoCmd:
		case kATADMAErr:
		default:
			{
				
				STATUS_LOG ( ( "IOATAPIProtocolTransport::SCSITaskCallbackFunction result = %ld.\n", result ) );
				SetRealizedDataTransferCount ( scsiTask, bytesTransferred );
				CompleteSCSITask ( 	scsiTask,
									kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE,
									kSCSITaskStatus_DeliveryFailure );
								
			}
			break;
			
	}
	
}


//--------------------------------------------------------------------------------------
//	CompleteSCSITask -	Called to complete a SCSI Command
//--------------------------------------------------------------------------------------

void
IOATAPIProtocolTransport::CompleteSCSITask ( 	SCSITaskIdentifier	request, 
												SCSIServiceResponse	serviceResponse,
												SCSITaskStatus		taskStatus )
{
	
	STATUS_LOG ( ( "IOATAPIProtocolTransport::CompleteSCSITask called\n" ) );

	RecordATAPITimeStamp (
		ATAPI_TRACE ( kATACompleteSCSICommand ),
		( unsigned int ) ( uintptr_t ) this,
		( unsigned int ) fATAUnitID,
		( unsigned int ) ( uintptr_t ) request,
		( serviceResponse << 8 ) | taskStatus );
	
	OSBitAndAtomic ( ~kATAPICommandBusyMask, &fSemaphore );
	CommandCompleted ( request, serviceResponse, taskStatus );
	
}


//--------------------------------------------------------------------------------------
//	AbortSCSICommand - Aborts a SCSI Command
//--------------------------------------------------------------------------------------

SCSIServiceResponse
IOATAPIProtocolTransport::AbortSCSICommand ( SCSITaskIdentifier request )
{

	RecordATAPITimeStamp (
		ATAPI_TRACE ( kATAAbort ),
		( unsigned int ) ( uintptr_t ) this,
		( unsigned int ) fATAUnitID,
		( unsigned int ) ( uintptr_t ) request );

	return kSCSIServiceResponse_SERVICE_DELIVERY_OR_TARGET_FAILURE;
}


//--------------------------------------------------------------------------------------
//	IsProtocolServiceSupported - Returns true if feature is supported
//--------------------------------------------------------------------------------------

bool
IOATAPIProtocolTransport::IsProtocolServiceSupported ( SCSIProtocolFeature feature,
													   void * serviceValue )
{
	
	bool	isSupported = false;
	
	STATUS_LOG ( ( "IOATAPIProtocolTransport::IsProtocolServiceSupported called\n" ) );
	
	switch ( feature )
	{
				
		case kSCSIProtocolFeature_ProtocolSpecificPolling:			
			// ATAPI supports low-power polling.
			isSupported = true;
			break;
		
		case kSCSIProtocolFeature_ProtocolSpecificSleepCommand:			
			// ATAPI supports ATA SLEEP command.
			isSupported = true;
			break;
			
		case kSCSIProtocolFeature_ProtocolSpecificPowerOff:
			// does platform support power off?
			isSupported = ( fMediaNotifyValue != kODDMediaNotifyValue0 );
			break;
		
		case kSCSIProtocolFeature_ACA:
			// ATAPI does not support Auto Contingent Allegiance
		case kSCSIProtocolFeature_CPUInDiskMode:
			// ATAPI does not support cpu in disk mode
		case kSCSIProtocolFeature_GetMaximumLogicalUnitNumber:
			// ATAPI does not support more than one logical unit.
		default:
			// Some other feature ATAPI doesn't know about, probably means
			// it isn't supported...
			break;
		
	}
	
	return isSupported;
	
}


//--------------------------------------------------------------------------------------
//	HandleProtocolServiceFeature - Returns true if feature is handled successfully
//--------------------------------------------------------------------------------------

bool
IOATAPIProtocolTransport::HandleProtocolServiceFeature ( SCSIProtocolFeature feature,
														 void * serviceValue )
{
	
	bool	isSupported = false;
	
	STATUS_LOG ( ( "IOATAPIProtocolTransport::HandleProtocolServiceFeature called\n" ) );
	
	switch ( feature )
	{
		
		case kSCSIProtocolFeature_ProtocolSpecificPolling:
			// We’re being told to do protocol specific polling
			if ( serviceValue != NULL )
			{
				
				UInt32	value = *( UInt32 * ) serviceValue;
				
				if ( value != 0 )
				{
					
					// start low power polling
					
					bool	resetOccurred = false;
					
					STATUS_LOG ( ( "Enabling polling of ATA Status register\n" ) );					
										
					fCommandGate->runAction ( ( IOCommandGate::Action )
											  &IOATAPIProtocolTransport::sSetWakeupResetOccurred,
											  ( void * ) resetOccurred );
					
					EnablePollingOfStatusRegister ( );
					isSupported = true;
					
				}
				
				if ( value == 0 )
				{
				
					// stop low power polling
					
					STATUS_LOG ( ( "Disabling polling of ATA Status register\n" ) );		
									
					DisablePollingOfStatusRegister ( );
					isSupported = true;
					
				}
				
			}
			
			break;
		
		case kSCSIProtocolFeature_ProtocolSpecificSleepCommand:
			
			// We’re being told to do protocol specific sleep
			
			if ( serviceValue != NULL )
			{
				
				UInt32	value = *( UInt32 * ) serviceValue;
				
				if ( value != 0 )
				{
					
					STATUS_LOG ( ( "Sending ATA sleep command\n" ) );				
					
					( void ) SendATASleepCommand ( );
					isSupported = true;
					
				}
				
			}
			
			break;
			
		case kSCSIProtocolFeature_ProtocolSpecificPowerOff:
			
			// We’re being told to cut power to the drive OFF
			
			RecordATAPITimeStamp ( ATAPI_TRACE ( kATAPowerOnReset ),
									( unsigned int ) ( uintptr_t ) this,
									( unsigned int ) fATAUnitID,
									( unsigned int ) fATADeviceType );			
			
			( void ) TurnDrivePowerOff ( );
			
			break;
			
		default:
			
			break;
		
	}
	
	return isSupported;
	
}


//--------------------------------------------------------------------------------------
//	HandlePowerOn -	Power management routine to handle power state transition
//--------------------------------------------------------------------------------------


IOReturn
IOATAPIProtocolTransport::HandlePowerOn ( void )
{
	
	IOReturn	status		= kIOReturnSuccess;
	bool		resetOccurred = false;
	
	
	STATUS_LOG ( ( "%s%s::%s called%s\n", "\033[36m",
				   getName ( ), __FUNCTION__, "\033[0m" ) );	
	
	fCommandGate->runAction ( ( IOCommandGate::Action )
							  &IOATAPIProtocolTransport::sCheckWakeupResetOccurred,
							  ( void * ) &resetOccurred );
	
	if ( !resetOccurred )
	{
		
		STATUS_LOG ( ( "%s fWakeUpResetOccurred is false, resetting device %s\n",
							"\033[36m", getName ( ), __FUNCTION__, "\033[0m" ) );	

		RecordATAPITimeStamp (
			ATAPI_TRACE ( kATAPowerOnReset ),
			( unsigned int ) ( uintptr_t ) this,
			( unsigned int ) fATAUnitID,
			( unsigned int ) fATADeviceType );
		
		// We aren't on a shared bus, so we need to reset the device
		status = ResetATAPIDevice ( );
		
	}
	
	else
	{
		
		STATUS_LOG ( ( "%s fWakeUpResetOccurred is true, NO reset needed %s\n",
				"\033[36m", getName ( ), __FUNCTION__, "\033[0m" ) );	

		RecordATAPITimeStamp (
			ATAPI_TRACE ( kATAPowerOnNoReset ),
			( unsigned int ) ( uintptr_t ) this,
			( unsigned int ) fATAUnitID,
			( unsigned int ) fATADeviceType );
		
	}
	
	return status;
	
}


//--------------------------------------------------------------------------------------
//	HandlePowerOff - Power managment routine to handle power state transition
//--------------------------------------------------------------------------------------


IOReturn
IOATAPIProtocolTransport::HandlePowerOff ( void )
{
	
	IOReturn		status = kIOReturnSuccess;
	bool			resetOccurred = false;

	RecordATAPITimeStamp (
		ATAPI_TRACE ( kATAHandlePowerOff ),
		( unsigned int ) ( uintptr_t ) this,
		( unsigned int ) fATAUnitID,
		( unsigned int ) fATADeviceType );
	
	fCommandGate->runAction ( ( IOCommandGate::Action )
							  &IOATAPIProtocolTransport::sSetWakeupResetOccurred,
							  ( void * ) resetOccurred );
	
	return status;
	
}


//--------------------------------------------------------------------------------------
//	sSCSITaskCallbackProc - static callback routine which calls through to the virtual
//							routine
//--------------------------------------------------------------------------------------

void
IOATAPIProtocolTransport::sSCSITaskCallbackProc ( IOATACommand * cmd )
{

	SCSITaskIdentifier			scsiTask;	
	ATAPIClientData				clientData;
	IOATAPIProtocolTransport *	self = NULL;

	STATUS_LOG ( ( "IOATAPIProtocolTransport::ATACallbackProc entering.\n" ) );	
	
	// Pull the clientData out of the command
	bcopy ( cmd->refCon, &clientData, sizeof ( clientData ) );
	
	// Get the scsiTask and a pointer to self from the clientData
	scsiTask 	= clientData.scsiTask;
	self 		= clientData.self;
	
	STATUS_LOG ( ( "IOATAPIProtocolTransport::ATACallbackProc calling virtual callback...\n" ) );	
	
	// Call through to virtual callback
	self->SCSITaskCallbackFunction ( cmd, scsiTask );
	
}


//---------------------------------------------------------------------------
// InspectDevice - Fetch information about the ATAPI device nub.
//---------------------------------------------------------------------------

bool
IOATAPIProtocolTransport::InspectDevice ( IOATADevice * ataDevice )
{
	
	OSString *		string			= NULL;
	IOReturn		theErr			= kIOReturnSuccess;
	
	// Fetch ATA device information from the nub.
	string = OSDynamicCast ( 	OSString,
								ataDevice->getProperty ( kATAVendorPropertyKey ) );
	
	if ( string != NULL )
	{
		
		strncpy ( fModel, string->getCStringNoCopy ( ), kSizeOfATAModelString );
		fModel[kSizeOfATAModelString] = '\0';
				
	}
	
	string = OSDynamicCast ( 	OSString,
								ataDevice->getProperty ( kATARevisionPropertyKey ) );
	
	if ( string != NULL )
	{
		
		strncpy ( fRevision, string->getCStringNoCopy ( ), kSizeOfATARevisionString );
		fRevision[kSizeOfATARevisionString] = '\0';
		
	}
	
	theErr = IdentifyAndConfigureATAPIDevice ( );
	
	if ( theErr != kIOReturnSuccess )
	{
		
		STATUS_LOG ( ( "IOATAPIProtocolTransport::IdentifyAndConfigureATAPIDevice theErr = %ld\n", ( UInt32 ) theErr ) );
		return false;
		
	}
	
	return true;
	
}


//--------------------------------------------------------------------------------------
//	sATACallbackSync - static synchronous callback routine
//--------------------------------------------------------------------------------------

void
IOATAPIProtocolTransport::sATACallbackSync ( IOATACommand * cmd )
{
	
	ATAPIConfigData *				configData;
	IOATAPIProtocolTransport *		self;
	
	STATUS_LOG ( ( "IOATAPIProtocolTransport::sATACallbackSync entering\n" ) );	
	
	configData = ( ATAPIConfigData * ) cmd->refCon;
	
	if ( cmd->getResult ( ) != kATANoErr )
	{
		
		STATUS_LOG ( ( "Command result error = %ld\n", cmd->getResult ( ) ) );
		
	}
	
	STATUS_LOG ( ( "signalling command completion\n" ) );	
	
	configData->done = true;
	
	self = configData->self;
	
	self->fCommandGate->commandWakeup ( configData, false );
	
}


//--------------------------------------------------------------------------------------
//	sATAPIConfigStateMachine - state machine for configuration commands
//--------------------------------------------------------------------------------------

void
IOATAPIProtocolTransport::sATAPIConfigStateMachine ( IOATACommand * cmd )
{
	
	ATAPIConfigData *				configData;
	IOATAPIProtocolTransport *		driver;
	IOReturn						status;
		
	STATUS_LOG ( ( "IOATAPIProtocolTransport::sATAPIConfigStateMachine entering\n" ) );	
	
	configData 	= ( ATAPIConfigData * ) cmd->refCon;
	status		= cmd->getResult ( );
	driver		= configData->self;
	
	switch ( configData->state )
	{
		
		case kPIOTransferModeSetup:
			configData->state = kPIOTransferModeDone;
			driver->SetPIOTransferMode ( cmd, false );
			break;
		
		case kPIOTransferModeDone:
			if ( ( driver->fUltraDMAMode != 0 ) || ( driver->fDMAMode != 0 ) )
			{
				configData->state = kDMATransferModeDone;
				driver->SetDMATransferMode ( cmd, false );
				break;
			}

		// Intentional fall through	in case device doesn't support DMA
		case kDMATransferModeDone:
			configData->done = true;
			driver->fCommandGate->commandWakeup ( configData, false );
			break;
			
		default:
			PANIC_NOW ( ( "sATAPIConfigStateMachine unexpected state\n" ) );
			break;
		
	}
		
}


//--------------------------------------------------------------------------------------
//	sATAPIResetCallback - static asynchronous callback routine for resets
//--------------------------------------------------------------------------------------

void
IOATAPIProtocolTransport::sATAPIResetCallback ( IOATACommand * cmd )
{
	
	IOATAPIProtocolTransport *	xptDriver;
	
	STATUS_LOG ( ( "IOATAPIProtocolTransport::sATAPIResetCallback entering\n" ) );	
	
	xptDriver = ( IOATAPIProtocolTransport * ) cmd->refCon;
	xptDriver->fWakeUpResetOccurred = true;
	xptDriver->fResetInProgress 	= false;

	RecordATAPITimeStamp (
		ATAPI_TRACE ( kATAResetComplete ),
		( unsigned int ) xptDriver->fATAUnitID );
	
}


//--------------------------------------------------------------------------------------
//	sATAPIVoidCallback - callback that does nothing
//--------------------------------------------------------------------------------------

void
IOATAPIProtocolTransport::sATAPIVoidCallback ( IOATACommand * cmd )
{
	return;	
}


//--------------------------------------------------------------------------------------
//	sPollStatusRegister - Callout method for thread_call_enter_delayed.
//--------------------------------------------------------------------------------------

void
IOATAPIProtocolTransport::sPollStatusRegister ( void * driver, void * refCon )
{
	
	IOATAPIProtocolTransport *	xptDriver;
	
	xptDriver = ( IOATAPIProtocolTransport * ) driver;
	xptDriver->PollStatusRegister ( refCon );
	
}


//--------------------------------------------------------------------------------------
//	sPollStatusRegisterCallback	- Callback method for PollStatusRegister().
//--------------------------------------------------------------------------------------

void
IOATAPIProtocolTransport::sPollStatusRegisterCallback ( IOATACommand * cmd )
{
	
	IOATAPIProtocolTransport *		xptDriver		= NULL;
	ATAPIClientData *				clientData		= NULL;
	
	clientData = ( ATAPIClientData * ) cmd->refCon;
	xptDriver = ( IOATAPIProtocolTransport * ) clientData->self;
	
	xptDriver->PollStatusRegisterCallback ( cmd );
	
}


//--------------------------------------------------------------------------------------
//	AllocateATACommandObjects - allocates ATA command objects
//--------------------------------------------------------------------------------------

void
IOATAPIProtocolTransport::AllocateATACommandObjects ( void )
{
	
	STATUS_LOG ( ( "IOATAPIProtocolTransport::AllocateATACommandObjects entering\n" ) );	
	
	IOATACommand *			cmd 		= NULL;
	ATAPIClientData *		clientData 	= NULL;
	ATAPIConfigData *		configData	= NULL;
	
	// First allocate our reserve command
	fResetCommand = fATADevice->allocCommand ( );
	assert ( fResetCommand != NULL );
	
	fConfigCommand = fATADevice->allocCommand ( );
	assert ( fConfigCommand != NULL );
	configData = ( ATAPIConfigData * ) IOMalloc ( sizeof ( ATAPIConfigData ) );
	assert ( configData != NULL );
	bzero ( configData, sizeof ( ATAPIConfigData ) );
	fConfigCommand->refCon = ( void * ) configData;
		
	fIdentifyCommand = fATADevice->allocCommand ( );
	assert ( fIdentifyCommand != NULL );
	clientData = ( ATAPIClientData * ) IOMalloc ( sizeof ( ATAPIClientData ) );
	assert ( clientData != NULL );
	bzero ( clientData, sizeof ( ATAPIClientData ) );
	fIdentifyCommand->refCon = ( void * ) clientData;
	
	for ( UInt32 index = 0; index < kIOATAPICommandPoolSize; index++ )
	{
		
		// Allocate the command
		cmd = fATADevice->allocCommand ( );
		assert ( cmd != NULL );
		
		// Allocate the command clientData
		clientData = ( ATAPIClientData * ) IOMalloc ( sizeof ( ATAPIClientData ) );
		assert ( clientData != NULL );
		bzero ( clientData, sizeof ( ATAPIClientData ) );
		
		// set the back pointers to each other
		cmd->refCon 	= ( void * ) clientData;
		clientData->cmd	= cmd;
		
		STATUS_LOG ( ( "adding command to pool\n" ) );
		
		// Enqueue the command in the free list
		fCommandPool->returnCommand ( cmd );
		
	}
	
	STATUS_LOG ( ( "IOATAPIProtocolTransport::AllocateATACommandObjects exiting\n" ) );	
	
}


//--------------------------------------------------------------------------------------
//	DeallocateATACommandObjects - deallocates ATA command objects
//--------------------------------------------------------------------------------------

void
IOATAPIProtocolTransport::DeallocateATACommandObjects ( void )
{
	
	STATUS_LOG ( ( "IOATAPIProtocolTransport::DellocateATACommandObjects entering\n" ) );	
	
	IOATACommand *		cmd 		= NULL;
	ATAPIClientData *	clientData 	= NULL;
	ATAPIConfigData *	configData	= NULL;
	
	cmd = ( IOATACommand * ) fCommandPool->getCommand ( false );
	assert ( cmd != NULL );
	
	//XXX Walk the in-use queue and abort the commands (potential memory leak right now)
	
	
	// This handles walking the free command queue
	while ( cmd != NULL )
	{
		
		clientData = ( ATAPIClientData * ) cmd->refCon;
		assert ( clientData != NULL );
		
		IOFree ( clientData, sizeof ( ATAPIClientData ) );
		clientData = NULL;
		
		fATADevice->freeCommand ( cmd );
		cmd = NULL;
		
		cmd = ( IOATACommand * ) fCommandPool->getCommand ( false );
		
	}
	
	configData = ( ATAPIConfigData * ) fConfigCommand->refCon;
	assert ( configData != NULL );
	IOFree ( configData, sizeof ( ATAPIConfigData ) );
	configData = NULL;
	
	clientData = ( ATAPIClientData * ) fIdentifyCommand->refCon;
	assert ( clientData != NULL );
	IOFree ( clientData, sizeof ( ATAPIClientData ) );
	clientData = NULL;
	
	// release "special" comands
	fATADevice->freeCommand ( fConfigCommand );
	fATADevice->freeCommand ( fResetCommand );
	fATADevice->freeCommand ( fIdentifyCommand );

	fConfigCommand 			= NULL;
	fResetCommand 			= NULL;
	fIdentifyCommand		= NULL;
	
	STATUS_LOG ( ( "IOATAPIProtocolTransport::DellocateATACommandObjects exiting\n" ) );	
	
}


//--------------------------------------------------------------------------------------
//	GetATACommandObject	- Gets an ata command object from the pool.
//--------------------------------------------------------------------------------------

IOATACommand *
IOATAPIProtocolTransport::GetATACommandObject ( bool blockForCommand )
{
	
	IOATACommand *		cmd	= NULL;
	
	STATUS_LOG ( ( "IOATAPIProtocolTransport::GetATACommandObject entering.\n" ) );
	
	cmd = ( IOATACommand * ) fCommandPool->getCommand ( blockForCommand );
	
	return cmd;
	
}


//--------------------------------------------------------------------------------------
//	ReturnATACommandObject - Returns the command to the command pool
//--------------------------------------------------------------------------------------

void
IOATAPIProtocolTransport::ReturnATACommandObject ( IOATACommand * cmd )
{
	
	STATUS_LOG ( ( "IOATAPIProtocolTransport::ReturnATACommandObject entering.\n" ) );
	
	assert ( cmd != NULL );
	fCommandPool->returnCommand ( cmd );
	
}


//--------------------------------------------------------------------------------------
//	IdentifyAndConfigureATAPIDevice	- 	Sends a device identify request to the device
//										and uses it to configure the drive speeds
//--------------------------------------------------------------------------------------

IOReturn
IOATAPIProtocolTransport::IdentifyAndConfigureATAPIDevice ( void )
{
	
	IOReturn						theErr				= kIOReturnSuccess;
	IOATABusInfo *					busInfoPtr			= NULL;
	IOATADevConfig *				deviceConfigPtr		= NULL;
	OSDictionary *					dict				= NULL;
	
	// Get some info about the ATA bus
	busInfoPtr = IOATABusInfo::atabusinfo ( );
	assert ( busInfoPtr != NULL );
	
	busInfoPtr->zeroData ( );
	theErr = fATADevice->provideBusInfo ( busInfoPtr );
	if ( theErr != kIOReturnSuccess )
	{
		
		STATUS_LOG ( ( "IOATAPIProtocolTransport::IdentifyAndConfigureATAPIDevice provide bus info failed thErr = %ld.\n", theErr ) );
		goto ReleaseBusInfoAndBail;
		
	}
	
	fATASocketType = busInfoPtr->getSocketType ( );
	STATUS_LOG ( ( "IOATAPIProtocolTransport::IdentifyAndConfigureATAPIDevice socket type = %d.\n", ( UInt8 ) fATASocketType ) );
			
	theErr = IdentifyATAPIDevice ( );
	if ( theErr != kIOReturnSuccess )
	{
		
		STATUS_LOG ( ( "IOATAPIProtocolTransport::IdentifyAndConfigureATAPIDevice IdentifyATAPIDevice error = %ld, resetting device.\n", theErr ) );
		
		theErr = ResetATAPIDevice ( );
		if ( theErr != kIOReturnSuccess )
		{
			
			STATUS_LOG ( ( "IOATAPIProtocolTransport::IdentifyAndConfigureATAPIDevice ResetATAPIDevice error = %ld.\n", theErr ) );
			
			// Not even a reset worked, bail
			goto ReleaseBusInfoAndBail;
			
		}
		
		theErr = IdentifyATAPIDevice ( );
		if ( theErr != kIOReturnSuccess )
		{
			
			STATUS_LOG ( ( "IOATAPIProtocolTransport::IdentifyAndConfigureATAPIDevice 2nd IdentifyATAPIDevice error = %ld.\n", theErr ) );

			// Not even a reset worked, bail
			goto ReleaseBusInfoAndBail;
		
		}
		
	}

	deviceConfigPtr = IOATADevConfig::atadevconfig ( );
	assert ( deviceConfigPtr != NULL );
	
	theErr = fATADevice->provideConfig ( deviceConfigPtr );
	if ( theErr != kIOReturnSuccess )
	{
		
		STATUS_LOG ( ( "IOATAPIProtocolTransport::IdentifyAndConfigureATAPIDevice provideConfig returned an error = %ld.\n", theErr ) );
		goto ReleaseBusInfoAndBail;
		
	}
	
	theErr = deviceConfigPtr->initWithBestSelection ( ( UInt16 * ) fDeviceIdentifyData, busInfoPtr );
	if ( theErr != kIOReturnSuccess )
	{
		
		STATUS_LOG ( ( "IOATAPIProtocolTransport::IdentifyAndConfigureATAPIDevice Autoconfigure didn't work error = %ld.\n", theErr ) );
		PANIC_NOW ( ( "Autoconfigure didn't work. Initialize drive speed manually.\n" ) );
		return theErr;
		
	}
		
	theErr = fATADevice->selectConfig ( deviceConfigPtr );
	if ( theErr != kIOReturnSuccess )
	{
		
		STATUS_LOG ( ( "IOATAPIProtocolTransport::IdentifyAndConfigureATAPIDevice selectConfig returned error = %ld.\n", theErr ) );
		return theErr;
		
	}

	fPIOMode 			= deviceConfigPtr->getPIOMode ( );
	fDMAMode			= deviceConfigPtr->getDMAMode ( );
	fUltraDMAMode 		= deviceConfigPtr->getUltraMode ( );
	fATAPIPacketConfig 	= deviceConfigPtr->getPacketConfig ( );
	
	// Adjust any of the Multiword DMA or Ultra DMA values if there is a subclass with an
	// ATAPI Mass Storage Characteristics dictionary.
	dict = OSDynamicCast ( OSDictionary, getProperty ( kIOPropertyATAPIMassStorageCharacteristics ) );
	if ( dict != NULL )
	{
		
		OSNumber *	modeNumber;
		
		STATUS_LOG ( ( "ATAPI Mass Storage dictionary exists.\n" ) );
		
		modeNumber = OSDynamicCast ( OSNumber, dict->getObject ( "DMA Mode" ) );
		if ( modeNumber != NULL )
		{
			
			STATUS_LOG ( ( "Changing default Multiword DMA Mode value from %d to %d\n",
							fDMAMode, modeNumber->unsigned8BitValue ( ) ) );
			fDMAMode = modeNumber->unsigned8BitValue ( );
			
		}

		modeNumber = OSDynamicCast ( OSNumber, dict->getObject ( "UDMA Mode" ) );
		if ( modeNumber != NULL )
		{
			
			STATUS_LOG ( ( "Changing default Ultra DMA Mode value from %d to %d\n",
							fUltraDMAMode, modeNumber->unsigned8BitValue ( ) ) );
			fUltraDMAMode = modeNumber->unsigned8BitValue ( );
			
		}
		
	}
	
	else
	{
		STATUS_LOG ( ( "ATAPI Mass Storage dictionary does not exist.\n" ) );
	}
	
	STATUS_LOG ( ( "atapiConfig = %d.\n", (int) fATAPIPacketConfig ) );	
	
	theErr = ConfigureATAPIDevice ( );
		
ReleaseBusInfoAndBail:


	if ( busInfoPtr != NULL )
	{

		STATUS_LOG ( ( "IOATAPIProtocolTransport::IdentifyAndConfigureATAPIDevice releasing bus info.\n" ) );
		busInfoPtr->release ( );
		busInfoPtr = NULL;
	
	}
	
	STATUS_LOG ( ( "IOATAPIProtocolTransport::IdentifyAndConfigureATAPIDevice returning theErr = %ld.\n", theErr ) );
	
	return theErr;
	
}


//--------------------------------------------------------------------------------------
//	IdentifyATAPIDevice	- 	Sends a device identify request to the device
//							and uses it to configure the drive speeds
//--------------------------------------------------------------------------------------

IOReturn
IOATAPIProtocolTransport::IdentifyATAPIDevice ( void )
{

	IOReturn	theErr = kIOReturnSuccess;
	
	STATUS_LOG ( ( "IOATAPIProtocolTransport::IdentifyATAPIDevice entering.\n" ) );
	
	// Zero the command object
	fIdentifyCommand->zeroCommand ( );
	
	// Start filling in the command
	fIdentifyCommand->setUnit ( fATAUnitID );
	fIdentifyCommand->setBuffer ( fDeviceIdentifyBuffer );
	fIdentifyCommand->setPosition ( 0 );
	fIdentifyCommand->setByteCount ( kATAPIIdentifyPacketDeviceDataSize );
	fIdentifyCommand->setTransferChunkSize ( kATADefaultSectorSize );
	
	fIdentifyCommand->setCommand ( kID_DRIVE );
	fIdentifyCommand->setTimeoutMS ( k10SecondTimeout );
	fIdentifyCommand->setFlags ( mATAFlagIORead );
	fIdentifyCommand->setOpcode ( kATAFnExecIO );
	// set the device head to the correct unit
	fIdentifyCommand->setDevice_Head ( fATAUnitID << 4 );
	fIdentifyCommand->setRegMask ( ( ataRegMask ) ( mATAErrFeaturesValid | mATAStatusCmdValid ) );
	fIdentifyCommand->setCallbackPtr ( &sATACallbackSync );
	
	STATUS_LOG ( ( "IOATAPIProtocolTransport::IdentifyATAPIDevice executing identify command.\n" ) );
	
	theErr = SendCommand ( fIdentifyCommand );
	
	#if defined(__BIG_ENDIAN__)
		// The identify device info needs to be byte-swapped on big-endian (ppc) 
		// systems because it is data that is produced by the drive, read across a 
		// 16-bit little-endian PCI interface, directly into a big-endian system.
		// Regular data doesn't need to be byte-swapped because it is written and 
		// read from the host and is intrinsically byte-order correct.	
		sSwapBytes16 ( ( UInt8 * ) fDeviceIdentifyData, kATAPIIdentifyPacketDeviceDataSize );
	#endif
	
	STATUS_LOG ( ( "IOATAPIProtocolTransport::IdentifyATAPIDevice exiting with theErr = %ld.\n", theErr ) );
	
	return theErr;
	
}


//--------------------------------------------------------------------------------------
//	ConfigureATAPIDevice - Configures the ATAPI Device
//--------------------------------------------------------------------------------------

IOReturn
IOATAPIProtocolTransport::ConfigureATAPIDevice ( void )
{
	
	IOReturn				status	= kIOReturnSuccess;
	ATAPIConfigData *		configData;
	
	STATUS_LOG ( ( "IOATAPIProtocolTransport::ConfigureATAPIDevice entering.\n" ) );
	
	configData = ( ATAPIConfigData * ) fConfigCommand->refCon;
	
	configData->self 	= this;
	configData->state 	= kPIOTransferModeSetup;
	configData->done 	= false;
	
	sATAPIConfigStateMachine ( fConfigCommand );	
	
	status = fCommandGate->runAction (
		OSMemberFunctionCast (
			IOCommandGate::Action,
			this,
			&IOATAPIProtocolTransport::GatedWaitForRequest ),
		configData );
	
	if ( status == kIOReturnSuccess )
	{
		status = fConfigCommand->getResult ( );
	}
	
	return kIOReturnSuccess;
		
}


//--------------------------------------------------------------------------------------
//	ReconfigureATAPIDevice - Reconfigures the ATAPI Device
//--------------------------------------------------------------------------------------

IOReturn
IOATAPIProtocolTransport::ReconfigureATAPIDevice ( void )
{
	
	if ( fConfigCommand != NULL )
	{
		
		SetPIOTransferMode ( fConfigCommand, true );
		
		if ( ( fUltraDMAMode != 0 ) || ( fDMAMode != 0 ) )
		{
			SetDMATransferMode ( fConfigCommand, true );
		}
		
	}
	
	return kIOReturnSuccess;
		
}


//--------------------------------------------------------------------------------------
//	SetPIOTransferMode - Configures the ATAPI Device's PIO Transfer mode
//--------------------------------------------------------------------------------------

IOReturn
IOATAPIProtocolTransport::SetPIOTransferMode ( IOATACommand * cmd, bool forceSync )
{
	
	IOReturn		theErr 	= kIOReturnSuccess;
	UInt8			mode	= 0;

	STATUS_LOG ( ( "IOATAPIProtocolTransport::SetPIOTransferMode entering.\n" ) );
	
	// Zero the command object
	cmd->zeroCommand ( );
	
	// Start filling in the command
	cmd->setUnit ( fATAUnitID );	
	cmd->setCommand ( kATAcmdSetFeatures );
	cmd->setTimeoutMS ( k10SecondTimeout );
	cmd->setFeatures ( kATASetTransferMode );
	
	// Always set to highest transfer mode
	mode = sConvertHighestBitToNumber ( fPIOMode );
	
	// PIO transfer mode is capped at 4 for now in the ATA-5 spec. If a device supports
	// more than mode 4 it has to at least support mode 4. We might not get the best
	// performance out of the drive, but it will work until we update to latest spec.
	if ( mode > 4 )
	{
		
		STATUS_LOG ( ( "IOATAPIProtocolTransport::SetPIOTransferMode mode > 4 = %ld.\n", ( UInt32 ) mode ) );
		mode = 4;
		
	}
	
	cmd->setSectorCount ( kATAEnablePIOModeMask | mode );
	cmd->setOpcode ( kATAFnExecIO );

	// set the device head to the correct unit
	cmd->setDevice_Head ( fATAUnitID << 4 );
	cmd->setFlags ( mATAFlagImmediate );
	
	if ( forceSync )
	{
		cmd->setCallbackPtr ( &sATAPIVoidCallback );
	}
	
	else
	{
		cmd->setCallbackPtr ( &sATAPIConfigStateMachine );
	}
	
	theErr = fATADevice->executeCommand ( cmd );	

	STATUS_LOG ( ( "IOATAPIProtocolTransport::SetPIOTransferMode exiting with error = %ld.\n", theErr ) );
	
	return theErr;
	
}


//--------------------------------------------------------------------------------------
//	SetDMATransferMode - Configures the ATAPI Device's DMA Transfer mode
//--------------------------------------------------------------------------------------

IOReturn
IOATAPIProtocolTransport::SetDMATransferMode ( IOATACommand * cmd, bool forceSync )
{
	
	IOReturn		theErr	= kIOReturnSuccess;
	UInt8			mode	= 0;

	STATUS_LOG ( ( "IOATAPIProtocolTransport::SetDMATransferMode entering.\n" ) );
		
	// Zero the command object
	cmd->zeroCommand ( );
	
	// Start filling in the command
	cmd->setUnit ( fATAUnitID );	
	cmd->setCommand ( kATAcmdSetFeatures );
	cmd->setTimeoutMS ( k10SecondTimeout );
	cmd->setFeatures ( kATASetTransferMode );
	cmd->setOpcode ( kATAFnExecIO );
	cmd->setDevice_Head ( fATAUnitID << 4 );
	cmd->setFlags ( mATAFlagImmediate );

	// Always set to highest transfer mode
	if ( fUltraDMAMode )
	{
		
		STATUS_LOG ( ( "IOATAPIProtocolTransport::SetDMATransferMode choosing UltraDMA.\n" ) );
		mode = sConvertHighestBitToNumber ( fUltraDMAMode );
		// Ultra DMA is capped at 4 for now in the ATA-5 spec. If a device supports
		// more than mode 4 it MUST at least support mode 4. We might not get the best
		// performance out of the drive, but it will work until we update to latest spec.
		if ( mode > 4 )
			mode = 4;
		
		cmd->setSectorCount ( kATAEnableUltraDMAModeMask | mode );
		
	}
	
	else
	{
		
		STATUS_LOG ( ( "IOATAPIProtocolTransport::SetDMATransferMode choosing DMA.\n" ) );
		mode = sConvertHighestBitToNumber ( fDMAMode );
		// MultiWord DMA is capped at 2 for now in the ATA-5 spec. If a device supports
		// more than mode 2 it MUST at least support mode 2. We might not get the best
		// performance out of the drive, but it will work until we update to latest spec.
		if ( mode > 2 )
			mode = 2;
		
		cmd->setSectorCount ( kATAEnableMultiWordDMAModeMask | mode );
		
	}
	
	if ( forceSync )
	{
		cmd->setCallbackPtr ( &sATAPIVoidCallback );
	}
	
	else
	{
		cmd->setCallbackPtr ( &sATAPIConfigStateMachine );
	}
		
	STATUS_LOG ( ( "IOATAPIProtocolTransport::SetDMATransferMode executing DMA setup command.\n" ) );
	
	theErr = fATADevice->executeCommand ( cmd );
	
	STATUS_LOG ( ( "IOATAPIProtocolTransport::SetDMATransferMode exiting with error = %ld.\n", theErr ) );
	
	return theErr;
	
}


//--------------------------------------------------------------------------------------
//	ResetATAPIDevice - Sends a device reset command to the device
//--------------------------------------------------------------------------------------

IOReturn
IOATAPIProtocolTransport::ResetATAPIDevice ( void )
{

	IOReturn	theErr = kIOReturnSuccess;
	
	STATUS_LOG ( ( "IOATAPIProtocolTransport::ResetATAPIDevice entering.\n" ) );
	
	if ( fResetInProgress )
		return kIOReturnNotPermitted;
	
	fResetInProgress = true;
	
	// Zero the command object
	fResetCommand->zeroCommand ( );
	
	// Start filling in the command
	fResetCommand->setUnit ( fATAUnitID );	
	fResetCommand->setCommand ( kSOFTRESET );
	fResetCommand->setTimeoutMS ( k45SecondTimeout );
	fResetCommand->setFlags ( mATAFlagImmediate );
	fResetCommand->setOpcode ( kATAFnBusReset );
	fResetCommand->setCallbackPtr ( &sATAPIResetCallback );
	fResetCommand->refCon = ( void * ) this;

	RecordATAPITimeStamp (
		ATAPI_TRACE ( kATAReset ),
		( unsigned int ) ( uintptr_t ) this,
		( unsigned int ) fATAUnitID );
	
	theErr = fATADevice->executeCommand ( fResetCommand );
	
	return theErr;
	
}


//--------------------------------------------------------------------------------------
//	EnablePollingOfStatusRegister - Called to schedule a poll of the status register.
//--------------------------------------------------------------------------------------

void
IOATAPIProtocolTransport::EnablePollingOfStatusRegister ( void )
{
	
	AbsoluteTime	time;
	
	STATUS_LOG ( ( "EnablePollingOfStatusRegister called\n" ) );
	
	RecordATAPITimeStamp ( ATAPI_TRACE ( kATAStartStatusPolling ),
							( unsigned int ) ( uintptr_t ) this,
							( unsigned int ) fATAUnitID,
							( unsigned int ) fATADeviceType );	
	
	// No reason to start a thread if we've been termintated	
	if ( ( isInactive ( ) == false ) &&
		 ( fPollingThread != NULL ) &&
		 ( fWakeUpResetOccurred == false ) )
	{
		
		// Retain ourselves so that this object doesn't go away
		// while we are polling
		
		retain ( );
		
		clock_interval_to_deadline ( 1000, kMillisecondScale, &time );
		thread_call_enter_delayed ( fPollingThread, time );
		
	}
	
}


//--------------------------------------------------------------------------------------
//	DisablePollingOfStatusRegister - Called to cancel a poll of the status register.
//--------------------------------------------------------------------------------------

void
IOATAPIProtocolTransport::DisablePollingOfStatusRegister ( void )
{
	
	fWakeUpResetOccurred = true;
	
	RecordATAPITimeStamp ( ATAPI_TRACE ( kATAStopStatusPolling ),
							( unsigned int ) ( uintptr_t ) this,
							( unsigned int ) fATAUnitID,
							( unsigned int ) fATADeviceType );		
	
	// Cancel the thread if it is scheduled.
	if ( thread_call_cancel ( fPollingThread ) )
	{
		
		// It was scheduled, so we balance out the retain ( )
		// with a release ( )
		release ( );
		
	}
	
}


//--------------------------------------------------------------------------------------
//	PollStatusRegister - 	Called to poll the status register to see if the drive
//							bay door has been opened.
//--------------------------------------------------------------------------------------

void
IOATAPIProtocolTransport::PollStatusRegister ( void * refCon )
{
	
	IOATACommand *		cmd;
	ATAPIClientData *	clientData;
	
	STATUS_LOG ( ( "PollStatusRegister called\n" ) );
	
	RecordATAPITimeStamp ( ATAPI_TRACE ( kATAStatusPoll ),
							( unsigned int ) ( uintptr_t ) this,
							( unsigned int ) fATAUnitID,
							( unsigned int ) fATADeviceType );	
	
	if ( fWakeUpResetOccurred == true )
		return;
	
	// Get a command
	cmd = fIdentifyCommand;
	
	clientData = ( ATAPIClientData * ) cmd->refCon;
	
	clientData->self = this;
	
	// Zero the command
	cmd->zeroCommand ( );
	
	// Set the command up for reading the register
	cmd->setFlags ( mATAFlagIORead );
	cmd->setOpcode ( kATAFnRegAccess );
	cmd->setUnit ( fATAUnitID );
	cmd->setRegMask ( mATAStatusCmdValid );
	cmd->setTimeoutMS ( k10SecondTimeout );
	cmd->setCallbackPtr ( &sPollStatusRegisterCallback );
	
	fATADevice->executeCommand ( cmd );
	
}


//--------------------------------------------------------------------------------------
//	PollStatusRegisterCallback - Callback handler for status register polling
//--------------------------------------------------------------------------------------

void
IOATAPIProtocolTransport::PollStatusRegisterCallback ( IOATACommand * cmd )
{
	
	IOReturn		theErr 			= kIOReturnSuccess;
	UInt8			statusRegValue	= 0;
	
	STATUS_LOG ( ( "IOATAPIProtocolTransport::PollStatusRegisterCallback called\n" ) );
	
	theErr = cmd->getResult ( );
	if ( theErr == kIOReturnSuccess )
	{
		
		// Read the status register value
		statusRegValue = cmd->getStatus ( );
		
		// If the value is 0x50, then the drive door has been opened since we last
		// checked. Let the SCSI Application Layer know so it can try to poll for
		// media.
		if ( statusRegValue == 0x50 )
		{

			STATUS_LOG ( ( "Sending message to application layer.\n" ) );
			
			// Reset the device to bring it out of sleep mode, since media
			// might have been inserted.
			ResetATAPIDevice ( );
			
		}
		
		else
		{
			
			// Do another poll
			EnablePollingOfStatusRegister ( );
			
		}
		
	}
	
	else
	{
		
		ERROR_LOG ( ( "Error = %d occurred while polling status register", theErr ) );
		// Some error occurred. For now, just issue another poll
		EnablePollingOfStatusRegister ( );
		
	}
	
	// Drop the retain for this poll
	release ( );
	
}


//--------------------------------------------------------------------------------------
//	SendATASleepCommand - Sends an ATA SLEEP command to the drive
//--------------------------------------------------------------------------------------

IOReturn
IOATAPIProtocolTransport::SendATASleepCommand ( void )
{
	
	IOReturn		status;
	IOATACommand *	cmd;
	
	STATUS_LOG ( ( "%s%s::%s called%s\n", "\033[36m", getName ( ), __FUNCTION__, "\033[0m" ) );	
	
	RecordATAPITimeStamp ( ATAPI_TRACE ( kATASendATASleepCmd ),
							( unsigned int ) ( uintptr_t ) this,
							( unsigned int ) fATAUnitID,
							( unsigned int ) fATADeviceType );		
	
	cmd = GetATACommandObject ( );
	
	// Zero the command
	cmd->zeroCommand ( );
	cmd->setUnit ( fATAUnitID );	
	cmd->setTimeoutMS ( kATATimeout10Seconds );
	cmd->setCallbackPtr ( &IOATAPIProtocolTransport::sATACallbackSync );
	cmd->setDevice_Head ( fATAUnitID << 4 );
	cmd->setOpcode ( kATAFnExecIO );
	cmd->setCommand ( kATAcmdSleep );
	
	status = SendCommand ( cmd );
	
	ReturnATACommandObject ( cmd );
	
	return status;	
	
}


//--------------------------------------------------------------------------------------
//	TurnDrivePowerOff - Called to turn power to the drive OFF.
//--------------------------------------------------------------------------------------

IOReturn
IOATAPIProtocolTransport::TurnDrivePowerOff ( void )
{
	
	IOReturn		status;
	IOATACommand *	cmd;
	
	STATUS_LOG ( ( "IOATAPIProtocolTransport::TurnDrivePowerOff called\n" ) );
	
	RecordATAPITimeStamp ( ATAPI_TRACE ( kATADriverPowerOff ),
							( unsigned int ) ( uintptr_t ) this,
							( unsigned int ) fATAUnitID );	
	
	cmd = GetATACommandObject ( );
	
	// Zero the command
	cmd->zeroCommand ( );
	
	// Set the command up for shutting the drive power off
	cmd->setUnit ( fATAUnitID );	
	cmd->setOpcode ( kATAFnRegAccess );
	cmd->setRegMask ( mATAStatusCmdValid );
	cmd->setFlags ( mATAFlagIORead | mATAFlagQuiesce );
	cmd->setTimeoutMS ( kATATimeout10Seconds );
	cmd->setCallbackPtr ( &IOATAPIProtocolTransport::sATACallbackSync );
	
	status = SendCommand ( cmd );
	
	ReturnATACommandObject ( cmd );
	
	return status;	
	
}


//--------------------------------------------------------------------------------------
//	SetWakeupResetOccurred - Called on safe side of command gate to set state
//--------------------------------------------------------------------------------------

void
IOATAPIProtocolTransport::SetWakeupResetOccurred ( bool resetOccurred )
{
	
	fWakeUpResetOccurred = resetOccurred;
	
}


//--------------------------------------------------------------------------------------
//	sSetWakeupResetOccurred - Called on safe side of command gate
//--------------------------------------------------------------------------------------

void
IOATAPIProtocolTransport::sSetWakeupResetOccurred ( IOATAPIProtocolTransport * driver,
												    bool resetOccurred )
{
	
	driver->SetWakeupResetOccurred ( resetOccurred );
	
}


//--------------------------------------------------------------------------------------
//	CheckWakeupResetOccurred -	Called on safe side of command gate to find out if
//								the driver has already received a reset message
//--------------------------------------------------------------------------------------

bool
IOATAPIProtocolTransport::CheckWakeupResetOccurred ( void )
{
	
	return fWakeUpResetOccurred;
	
}


//--------------------------------------------------------------------------------------
//	sCheckWakeupResetOccurred - Called on safe side of command gate
//--------------------------------------------------------------------------------------

void
IOATAPIProtocolTransport::sCheckWakeupResetOccurred ( IOATAPIProtocolTransport * driver,
												 	  bool * resetOccurred )
{
	
	*resetOccurred = driver->CheckWakeupResetOccurred ( );
	return;
	
}


//--------------------------------------------------------------------------------------
//	sSwapBytes16 - Swaps the buffer for device identify data
//--------------------------------------------------------------------------------------

void
IOATAPIProtocolTransport::sSwapBytes16 ( UInt8 * buffer, IOByteCount numBytesToSwap )
{
	
	IOByteCount		index;
	UInt8			temp;
	UInt8 *			firstBytePtr;
	
	for ( index = 0; index < numBytesToSwap; index += 2 )
	{
		
		firstBytePtr 	= buffer;				// save pointer
		temp 			= *buffer++;			// Save Byte0, point to Byte1
		*firstBytePtr 	= *buffer;				// Byte0 = Byte1
		*buffer++		= temp;					// Byte1 = Byte0
		
	}
	
}


//--------------------------------------------------------------------------------------
//	sConvertHighestBitToNumber - Finds the higest bit in a number and returns
//--------------------------------------------------------------------------------------

UInt8
IOATAPIProtocolTransport::sConvertHighestBitToNumber ( UInt16 bitField )
{
	
	UInt16  index, integer;
	
	// Test all bits from left to right, terminating at the first non-zero bit
	for ( index = 0x0080, integer = 7; ( ( index & bitField ) == 0 && index != 0 ) ; index >>= 1, integer-- )
	{ ; }
	
	return ( integer );
	
}


//---------------------------------------------------------------------------
// Handles messages from our provider.
//---------------------------------------------------------------------------

IOReturn
IOATAPIProtocolTransport::message ( UInt32 type, IOService * provider, void * argument )
{
	
	IOReturn		status = kIOReturnSuccess;
	
	STATUS_LOG ( ( "IOATABlockStorageDevice::message %p %lx\n", this, type ) );
	

	switch ( type )
	{
		
		case kATAResetEvent:					// Someone gave a reset to the bus
			// reconfig device here
			fWakeUpResetOccurred = true;
			status = ReconfigureATAPIDevice ( );
			// Tell the layer above us that a reset occurred so it can lock media
			// and verify that the device is in a good state.
			SendNotification_VerifyDeviceState ( );
			break;
		
		case kATANewMediaEvent:
			// Tell the layer above us that new media has been added so it can lock media
			// and verify that the device is in a good state.
			SendNotification_VerifyDeviceState ( );
			break;
			
		case kATANullEvent:						// Just kidding -- nothing happened
			break;
		
		// atapi resets are not relevent to ATA devices, but soft-resets ARE relevant to ATAPI devices.
		case kATAPIResetEvent:					// Someone gave a ATAPI reset to the drive
			// reconfig device here
			fWakeUpResetOccurred = true;
			status = ReconfigureATAPIDevice ( );
			// Tell the layer above us that a reset occurred so it can lock media
			// and verify that the device is in a good state.
			SendNotification_VerifyDeviceState ( );
			break;
		
		case kIOMessageServiceIsRequestingClose:
            fPhysicallyConnected = false;
			SendNotification_DeviceRemoved ( );
            DeallocateATACommandObjects ( );
            if ( fATADevice != NULL )
            {
				// Make sure we close provider, else the terminate won't propagate up the
				// stack.
                fATADevice->close ( this );
                fATADevice = NULL;
				
            }
			break;
			
		default:
			status = super::message ( type, provider, argument );
			break;
		
	}
	
	return status;
	
}


//------------------------------------------------------------------------------
//	RecordATAPITimeStamp												[STATIC]
//------------------------------------------------------------------------------

static inline void
RecordATAPITimeStamp (
        unsigned int code,
        unsigned int a, unsigned int b,
        unsigned int c, unsigned int d )
{
	
	if ( gATAPIDebugFlags != 0 )
	{
		IOTimeStampConstant ( code, a, b, c, d );
	}
	
}


//---------------------------------------------------------------------------
//	SendCommand	-	Executes comand and waits for its completion.
//---------------------------------------------------------------------------

IOReturn
IOATAPIProtocolTransport::SendCommand ( IOATACommand * cmd )
{
	
	IOReturn			status;
	void *				previousRefCon;
	ATAPIConfigData		configData;
	
	STATUS_LOG ( ( "IOATAPIProtocolTransport::SendCommand\n" ) );
	
	previousRefCon = cmd->refCon;
	
	cmd->refCon = ( void * ) &configData;
	configData.self	= this;
	configData.done	= false;
	
	status = fATADevice->executeCommand ( cmd );
	
	STATUS_LOG ( ( "executeCommand returned 0x%0lX\n", status ) );
	
	if ( status == kIOReturnSuccess )
	{
		
		status = fCommandGate->runAction (
			OSMemberFunctionCast (
				IOCommandGate::Action,
				this,
				&IOATAPIProtocolTransport::GatedWaitForRequest ),
			&configData );
		
		STATUS_LOG ( ( "runAction returned 0x%0lX\n", status ) );
		
		if ( status == kIOReturnSuccess )
		{
			
			status = cmd->getResult ( );
			
			STATUS_LOG ( ( "getResult returned 0x%0lX\n", status ) );
			
		}
		
	}
	
	cmd->refCon = previousRefCon;
	
	return status;
	
}


//---------------------------------------------------------------------------
//	GatedWaitForRequest	-	Wait for command completion.
//---------------------------------------------------------------------------

IOReturn
IOATAPIProtocolTransport::GatedWaitForRequest ( void * data )
{
	
	IOReturn			status		= kIOReturnSuccess;
	ATAPIConfigData *	configData	= NULL;
	
	STATUS_LOG ( ( "IOATAPIProtocolTransport::GatedWaitForRequest\n" ) );
	
	configData = ( ATAPIConfigData * ) data;
	
	// Check if the request has completed already
	while ( configData->done == false )
	{
		
		STATUS_LOG ( ( "will wait for command completion\n" ) );
		
		// Wait for the completion...
		status = fCommandGate->commandSleep ( configData, THREAD_UNINT );
		
		if ( status != THREAD_AWAKENED )
		{
			panic ( "IOATAPIProtocolTransport: detected spurious wakeup - status = 0x%08x\n", status );
		}
		
	}
	
	STATUS_LOG ( ( "command completed\n" ) );
	
	return status;
	
}


// Binary compatibility reserved method space
OSMetaClassDefineReservedUnused ( IOATAPIProtocolTransport, 1 );
OSMetaClassDefineReservedUnused ( IOATAPIProtocolTransport, 2 );
OSMetaClassDefineReservedUnused ( IOATAPIProtocolTransport, 3 );
OSMetaClassDefineReservedUnused ( IOATAPIProtocolTransport, 4 );
OSMetaClassDefineReservedUnused ( IOATAPIProtocolTransport, 5 );
OSMetaClassDefineReservedUnused ( IOATAPIProtocolTransport, 6 );
OSMetaClassDefineReservedUnused ( IOATAPIProtocolTransport, 7 );
OSMetaClassDefineReservedUnused ( IOATAPIProtocolTransport, 8 );
OSMetaClassDefineReservedUnused ( IOATAPIProtocolTransport, 9 );
OSMetaClassDefineReservedUnused ( IOATAPIProtocolTransport, 10 );
OSMetaClassDefineReservedUnused ( IOATAPIProtocolTransport, 11 );
OSMetaClassDefineReservedUnused ( IOATAPIProtocolTransport, 12 );
OSMetaClassDefineReservedUnused ( IOATAPIProtocolTransport, 13 );
OSMetaClassDefineReservedUnused ( IOATAPIProtocolTransport, 14 );
OSMetaClassDefineReservedUnused ( IOATAPIProtocolTransport, 15 );
OSMetaClassDefineReservedUnused ( IOATAPIProtocolTransport, 16 );


//--------------------------------------------------------------------------------------
//							End				Of				File
//--------------------------------------------------------------------------------------