/*
 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 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@
 */


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

// Private includes
#include "ATASMARTUserClient.h"
#include "ATASMARTLib.h"

// IOKit includes
#include <IOKit/IOBufferMemoryDescriptor.h>
#include <IOKit/IOCommandGate.h>
#include <IOKit/IOWorkLoop.h>

// IOKit ATA includes
#include <IOKit/ata/IOATADevice.h>
#include <IOKit/ata/IOATATypes.h>
#include <IOKit/ata/IOATACommand.h>

#include "IOATABlockStorageDevice.h"


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

// For debugging, set ATA_SMART_USER_CLIENT_DEBUGGING_LEVEL to one
// of the following values:
//		0	No debugging 	(GM release level)
// 		1 	PANIC_NOW only
//		2	PANIC_NOW and ERROR_LOG
//		3	PANIC_NOW, ERROR_LOG and STATUS_LOG
#define ATA_SMART_USER_CLIENT_DEBUGGING_LEVEL 0

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

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

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

#define super IOUserClient
OSDefineMetaClassAndStructors ( ATASMARTUserClient, IOUserClient );


#if 0
#pragma mark -
#pragma mark Constants
#endif


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	Enums
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

// S.M.A.R.T command code
enum
{
	kATAcmdSMART	= 0xB0
};

// S.M.A.R.T command features register codes
enum
{
	kFeaturesRegisterReadData					= 0xD0,
	kFeaturesRegisterReadDataThresholds			= 0xD1,
	kFeaturesRegisterEnableDisableAutoSave		= 0xD2,
	// Reserved
	kFeaturesRegisterExecuteOfflineImmed		= 0xD4,
	kFeaturesRegisterReadLogAtAddress			= 0xD5,
	kFeaturesRegisterWriteLogAtAddress			= 0xD6,
	// Reserved
	kFeaturesRegisterEnableOperations			= 0xD8,
	kFeaturesRegisterDisableOperations			= 0xD9,
	kFeaturesRegisterReturnStatus				= 0xDA
};

// S.M.A.R.T 'magic' values
enum
{
	kSMARTMagicCylinderLoValue	= 0x4F,
	kSMARTMagicCylinderHiValue	= 0xC2
};

// S.M.A.R.T Return Status validity values
enum
{
	kSMARTReturnStatusValidLoValue	= 0xF4,
	kSMARTReturnStatusValidHiValue	= 0x2C
};

// S.M.A.R.T Auto-Save values
enum
{
	kSMARTAutoSaveEnable	= 0xF1,
	kSMARTAutoSaveDisable	= 0x00
};

enum
{
	kATAThirtySecondTimeoutInMS	= 30000
};


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

IOExternalMethod
ATASMARTUserClient::sMethods[kIOATASMARTMethodCount] =
{
	{
		// Method #0 EnableDisableOperations
		0,
		( IOMethod ) &ATASMARTUserClient::EnableDisableOperations,
		kIOUCScalarIScalarO,
		1,
		0
	},
	{
		// Method #1 EnableDisableAutoSave
		0,
		( IOMethod ) &ATASMARTUserClient::EnableDisableAutoSave,
		kIOUCScalarIScalarO,
		1,
		0
	},
	{
		// Method #2 ReturnStatus
		0,
		( IOMethod ) &ATASMARTUserClient::ReturnStatus,
		kIOUCScalarIScalarO,
		0,
		1
	},
	{
		// Method #3 ExecuteOfflineImmediate
		0,
		( IOMethod ) &ATASMARTUserClient::ExecuteOfflineImmediate,
		kIOUCScalarIScalarO,
		1,
		0
	},
	{
		// Method #4 ReadData
		0,
		( IOMethod ) &ATASMARTUserClient::ReadData,
		kIOUCScalarIScalarO,
		1,
		0
	},
	{
		// Method #5 ReadDataThresholds
		0,
		( IOMethod ) &ATASMARTUserClient::ReadDataThresholds,
		kIOUCScalarIScalarO,
		1,
		0
	},
	{
		// Method #6 ReadLogAtAddress
		0,
		( IOMethod ) &ATASMARTUserClient::ReadLogAtAddress,
		kIOUCScalarIStructI,
		0,
		sizeof ( ATASMARTReadLogStruct )
	},
	{
		// Method #7 WriteLogAtAddress
		0,
		( IOMethod ) &ATASMARTUserClient::WriteLogAtAddress,
		kIOUCScalarIStructI,
		0,
		sizeof ( ATASMARTWriteLogStruct )
	},
	{
		// Method #8 GetIdentifyData
		0,
		( IOMethod ) &ATASMARTUserClient::GetIdentifyData,
		kIOUCStructIStructO,
		sizeof ( ATAGetIdentifyDataStruct ),
		sizeof ( UInt32 )
	}
	
};


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


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ init - Initializes member variables							[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ


bool
ATASMARTUserClient::init ( OSDictionary * dictionary )
{
	
	STATUS_LOG ( ( "ATASMARTUserClient::init\n" ) );
	
	if ( !super::init ( dictionary ) )
		return false;
	
	fTask 					= NULL;
	fProvider	 			= NULL;
	fOutstandingCommands	= 0;
	
	return true;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ start - Start providing services								[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

bool
ATASMARTUserClient::start ( IOService * provider )
{
	
	IOWorkLoop *	workLoop = NULL;
	
	STATUS_LOG ( ( "ATASMARTUserClient::start\n" ) );
	
	if ( fProvider != NULL )
	{
		
		ERROR_LOG ( ( "fProvider != NULL, returning false\n" ) );
		return false;
		
	}
	
	STATUS_LOG ( ( "assigning fProvider\n" ) );	
	fProvider = OSDynamicCast ( IOATABlockStorageDevice, provider );
	if ( fProvider == NULL )
	{
		
		ERROR_LOG ( ( "Provider not IOATABlockStorageDevice\n" ) );
		return false;
		
	}
	
	if ( !super::start ( provider ) )
	{
		
		ERROR_LOG ( ( "super rejected provider in start\n" ) );
		return false;
		
	}
	
	STATUS_LOG ( ( "Creating command gate\n" ) );
	fCommandGate = IOCommandGate::commandGate ( this );
	if ( fCommandGate == NULL )
	{
		
		ERROR_LOG ( ( "Command gate creation failed\n" ) );
		return false;
		
	}

	workLoop = getWorkLoop ( );
	if ( workLoop == NULL )
	{
		
		ERROR_LOG ( ( "workLoop == NULL\n" ) );
		fCommandGate->release ( );
		fCommandGate = NULL;
		return false;
		
	}
	
	STATUS_LOG ( ( "Adding command gate\n" ) );
	workLoop->addEventSource ( fCommandGate );
	
	STATUS_LOG ( ( "Opening provider\n" ) );
	if ( !fProvider->open ( this, kIOATASMARTUserClientAccessMask, 0 ) )
	{
		
		ERROR_LOG ( ( "Open failed\n" ) );
		fCommandGate->release ( );
		fCommandGate = NULL;
		return false;
		
	}
	
	fWorkLoop = workLoop;
	
	STATUS_LOG ( ( "start done\n" ) );
	
	// Yes, we found an object to use as our interface
	return true;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ initWithTask - Save task_t and validate the connection type	[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

bool
ATASMARTUserClient::initWithTask ( task_t	owningTask,
								   void *	securityToken,
								   UInt32	type )
{
	
	STATUS_LOG ( ( "ATASMARTUserClient::initWithTask called\n" ) );
	
	if ( type != kIOATASMARTLibConnection )
		return false;
	
	fTask = owningTask;
	return true;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ clientClose - Close down services.							[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
ATASMARTUserClient::clientClose ( void )
{
	
	STATUS_LOG ( ( "clientClose called\n" ) );
	
	if ( fProvider != NULL )
		HandleTerminate ( fProvider );
	
	STATUS_LOG ( ( "Done\n" ) );
	
	return super::clientClose ( );
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ free - Releases any items we need to release.					[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

void
ATASMARTUserClient::free ( void )
{
	
	// Remove the command gate from the workloop
	if ( fWorkLoop != NULL )
	{
		
		fWorkLoop->removeEventSource ( fCommandGate );
		fWorkLoop = NULL;
		
	}
	
	// Release the command gate
	if ( fCommandGate != NULL )
	{
		
		fCommandGate->release ( );
		fCommandGate = NULL;
		
	}
	
	super::free ( );
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ message - Handles termination messages.						[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
ATASMARTUserClient::message ( UInt32 type, IOService * provider, void * arg )
{
	
	IOReturn	status = kIOReturnSuccess;
	
	STATUS_LOG ( ( "message called\n" ) );
	
	STATUS_LOG ( ( "type = %ld, provider = %p\n", type, provider ) );
	
	switch ( type )
	{
		
		case kIOMessageServiceIsRequestingClose:
			break;
			
		case kIOMessageServiceIsTerminated:
			
			STATUS_LOG ( ( "kIOMessageServiceIsTerminated called\n" ) );
			status = HandleTerminate ( provider );
			break;
			
		default:
			status = super::message ( type, provider, arg );
			break;
		
	}
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ EnableDisableOperations - Enables/Disables SMART operations.	[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
ATASMARTUserClient::EnableDisableOperations ( UInt32 enable )
{
	
	IOReturn		status = kIOReturnSuccess;
	IOATACommand *	command;
	
	STATUS_LOG ( ( "EnableDisableOperations called\n" ) );
	
	fOutstandingCommands++;
	
	if ( isInactive ( ) )
	{
		
		status = kIOReturnNoDevice;
		goto ErrorExit;
		
	}
	
	fProvider->retain ( );
	
	command = AllocateCommand ( );
	if ( command == NULL )
	{
		
		status = kIOReturnNoResources;
		goto ReleaseProvider;
		
	}
	
	if ( enable == 0 )
	{
		
		// They want to disable SMART operations.
		command->setFeatures ( kFeaturesRegisterDisableOperations );
		
	}
	
	else
	{
		
		// The want to enable SMART operations.
		command->setFeatures ( kFeaturesRegisterEnableOperations );
		
	}
	
	command->setOpcode		( kATAFnExecIO );
	command->setTimeoutMS	( kATAThirtySecondTimeoutInMS );
	command->setCylLo		( kSMARTMagicCylinderLoValue );
	command->setCylHi		( kSMARTMagicCylinderHiValue );
	command->setCommand		( kATAcmdSMART );
	
	status = SendSMARTCommand ( command );
	if ( status == kIOReturnIOError )
	{
		
		if ( command->getEndErrorReg ( ) & 0x04 )
		{
			
			ERROR_LOG ( ( "Enable/Disable unsupported\n" ) );
			status = kIOReturnUnsupported;
			
		}
		
	}
	
	DeallocateCommand ( command );
	command = NULL;
	
	
ReleaseProvider:
	
	
	fProvider->release ( );
	
	
ErrorExit:
	
	
	fOutstandingCommands--;
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ EnableDisableAutoSave - Enables/Disables SMART autosave.		[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
ATASMARTUserClient::EnableDisableAutoSave ( UInt32 enable )
{
	
	IOReturn		status = kIOReturnSuccess;
	IOATACommand *	command;
	
	STATUS_LOG ( ( "EnableDisableAutoSave called\n" ) );
	
	fOutstandingCommands++;
	
	if ( isInactive ( ) )
	{
		
		status = kIOReturnNoDevice;
		goto ErrorExit;
		
	}
	
	fProvider->retain ( );
	
	command = AllocateCommand ( );
	if ( command == NULL )
	{
		
		status = kIOReturnNoResources;
		goto ReleaseProvider;
		
	}
	
	if ( enable == 0 )
	{
		
		// They want to disable SMART autosave operations.
		command->setSectorCount ( kSMARTAutoSaveDisable );
		
	}
	
	else
	{
		
		// They want to enable SMART autosave operations.
		command->setSectorCount ( kSMARTAutoSaveEnable );
		
	}
	
	command->setFeatures 	( kFeaturesRegisterEnableDisableAutoSave );
	command->setOpcode		( kATAFnExecIO );
	command->setTimeoutMS	( kATAThirtySecondTimeoutInMS );
	command->setCylLo		( kSMARTMagicCylinderLoValue );
	command->setCylHi		( kSMARTMagicCylinderHiValue );
	command->setCommand		( kATAcmdSMART );
	
	status = SendSMARTCommand ( command );
	if ( status == kIOReturnIOError )
	{
		
		if ( command->getEndErrorReg ( ) & 0x04 )
		{
			
			ERROR_LOG ( ( "Enable/Disable autosave unsupported\n" ) );
			status = kIOReturnUnsupported;
			
		}
		
	}
	
	DeallocateCommand ( command );
	command = NULL;
	
	
ReleaseProvider:
	
	
	fProvider->release ( );
	
	
ErrorExit:
	
	
	fOutstandingCommands--;
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ ReturnStatus - Returns SMART status.							[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
ATASMARTUserClient::ReturnStatus ( UInt32 * exceededCondition )
{
	
	IOReturn		status 	= kIOReturnSuccess;
	IOATACommand *	command	= NULL;
	UInt8			lbaMid 	= kSMARTMagicCylinderLoValue;
	UInt8			lbaHigh	= kSMARTMagicCylinderHiValue;

	STATUS_LOG ( ( "ReturnStatus called\n" ) );
	
	fOutstandingCommands++;
	
	if ( isInactive ( ) )
	{
		
		status = kIOReturnNoDevice;
		goto ErrorExit;
		
	}
	
	fProvider->retain ( );
	
	command = AllocateCommand ( );
	if ( command == NULL )
	{
		
		status = kIOReturnNoResources;
		goto ReleaseProvider;
		
	}
		
	command->setFeatures 	( kFeaturesRegisterReturnStatus );
	command->setOpcode		( kATAFnExecIO );
	command->setTimeoutMS	( kATAThirtySecondTimeoutInMS );
	command->setCylLo		( lbaMid );
	command->setCylHi		( lbaHigh );
	command->setCommand		( kATAcmdSMART );
	command->setRegMask		( ( ataRegMask ) ( mATACylinderHiValid | mATACylinderLoValid ) );
	command->setFlags		( mATAFlagTFAccessResult );
	
	status = SendSMARTCommand ( command );
	
	lbaMid 	= command->getCylLo ( );
	lbaHigh = command->getCylHi ( );
	
	if ( status == kIOReturnSuccess )
	{
	
		// Check if threshold exceeded
		if ( ( lbaMid == kSMARTReturnStatusValidLoValue ) &&
			 ( lbaHigh == kSMARTReturnStatusValidHiValue ) )
		{
			*exceededCondition = 1;
		}
		
		else
		{
			*exceededCondition = 0;
		}
	
	}
	
	if ( status == kIOReturnIOError )
	{
		
		if ( command->getEndErrorReg ( ) & 0x04 )
		{
			
			ERROR_LOG ( ( "Return Status unsupported\n" ) );
			status = kIOReturnUnsupported;
			
		}
		
	}
	
	DeallocateCommand ( command );
	command = NULL;
	
	
ReleaseProvider:
	
	
	fProvider->release ( );
	
	
ErrorExit:
	
	
	fOutstandingCommands--;
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ ExecuteOfflineImmediate - Executes self test offline immediately. [PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
ATASMARTUserClient::ExecuteOfflineImmediate ( UInt32 extendedTest )
{
	
	IOReturn		status 	= kIOReturnSuccess;
	IOATACommand *	command	= NULL;
	
	STATUS_LOG ( ( "ExecuteOfflineImmediate called\n" ) );
	
	fOutstandingCommands++;
	
	if ( isInactive ( ) )
	{
		
		status = kIOReturnNoDevice;
		goto ErrorExit;
		
	}
	
	fProvider->retain ( );
	
	command = AllocateCommand ( );
	if ( command == NULL )
	{
		
		status = kIOReturnNoResources;
		goto ReleaseProvider;
		
	}
		
	command->setFeatures 		( kFeaturesRegisterExecuteOfflineImmed );
	command->setOpcode			( kATAFnExecIO );
	command->setTimeoutMS		( kATAThirtySecondTimeoutInMS );
	command->setSectorNumber	( ( extendedTest == 0 ) ? 0x01 : 0x02 );
	command->setCylLo			( kSMARTMagicCylinderLoValue );
	command->setCylHi			( kSMARTMagicCylinderHiValue );
	command->setCommand			( kATAcmdSMART );
	
	status = SendSMARTCommand ( command );
	if ( status == kIOReturnIOError )
	{
		
		if ( command->getEndErrorReg ( ) & 0x04 )
		{
			
			ERROR_LOG ( ( "Execute Offline Immediate unsupported\n" ) );
			status = kIOReturnUnsupported;
			
		}
		
	}
	
	DeallocateCommand ( command );
	command = NULL;
	
	
ReleaseProvider:
	
	
	fProvider->release ( );
	
	
ErrorExit:
	
	
	fOutstandingCommands--;
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ ReadData - Reads SMART data. 									   [PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
ATASMARTUserClient::ReadData ( vm_address_t data )
{
	
	IOReturn				status 	= kIOReturnSuccess;
	IOATACommand *			command	= NULL;
	IOMemoryDescriptor *	buffer	= NULL;
	
	STATUS_LOG ( ( "ReadData called\n" ) );
	
	fOutstandingCommands++;
	
	if ( isInactive ( ) )
	{
		
		status = kIOReturnNoDevice;
		goto ErrorExit;
		
	}
	
	fProvider->retain ( );
	
	command = AllocateCommand ( );
	if ( command == NULL )
	{
		
		status = kIOReturnNoResources;
		goto ReleaseProvider;
		
	}
	
	buffer = IOMemoryDescriptor::withAddress ( 	data,
												sizeof ( ATASMARTData ),
												kIODirectionIn,
												fTask );
	
	if ( buffer == NULL )
	{
		
		status = kIOReturnNoResources;
		goto ReleaseCommand;
		
	}
	
	status = buffer->prepare ( );
	if ( status != kIOReturnSuccess )
	{
		
		goto ReleaseBuffer;
		
	}
	
	command->setBuffer 			( buffer );
	command->setByteCount 		( sizeof ( ATASMARTData ) );
	command->setFeatures 		( kFeaturesRegisterReadData );
	command->setOpcode			( kATAFnExecIO );
	command->setTimeoutMS		( kATAThirtySecondTimeoutInMS );
	command->setCylLo			( kSMARTMagicCylinderLoValue );
	command->setCylHi			( kSMARTMagicCylinderHiValue );
	command->setCommand			( kATAcmdSMART );
	command->setFlags 			( mATAFlagIORead );
	
	status = SendSMARTCommand ( command );
	if ( status == kIOReturnIOError )
	{
		
		if ( command->getEndErrorReg ( ) & 0x04 )
		{
			
			ERROR_LOG ( ( "ReadData unsupported\n" ) );
			status = kIOReturnUnsupported;
			
		}
		
		if ( command->getEndErrorReg ( ) & 0x10 )
		{
			
			ERROR_LOG ( ( "ReadData Not readable\n" ) );
			status = kIOReturnNotReadable;
			
		}
		
	}
	
	buffer->complete ( );
	
	
ReleaseBuffer:
	
	
	buffer->release ( );
	buffer = NULL;
	
	
ReleaseCommand:
	
	
	DeallocateCommand ( command );
	command = NULL;
	
	
ReleaseProvider:
	
	
	fProvider->release ( );
	
	
ErrorExit:
	
	
	fOutstandingCommands--;
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ ReadDataThresholds - Reads SMART data thresholds. 			   [PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
ATASMARTUserClient::ReadDataThresholds ( vm_address_t data )
{
	
	IOReturn				status 	= kIOReturnSuccess;
	IOATACommand *			command	= NULL;
	IOMemoryDescriptor *	buffer	= NULL;
	
	STATUS_LOG ( ( "ReadDataThresholds called\n" ) );
	
	fOutstandingCommands++;
	
	if ( isInactive ( ) )
	{
		
		status = kIOReturnNoDevice;
		goto ErrorExit;
		
	}
	
	fProvider->retain ( );
	
	command = AllocateCommand ( );
	if ( command == NULL )
	{
		
		status = kIOReturnNoResources;
		goto ReleaseProvider;
		
	}
	
	buffer = IOMemoryDescriptor::withAddress ( 	data,
												sizeof ( ATASMARTDataThresholds ),
												kIODirectionIn,
												fTask );
	
	if ( buffer == NULL )
	{
		
		status = kIOReturnNoResources;
		goto ReleaseCommand;
		
	}
	
	status = buffer->prepare ( );
	if ( status != kIOReturnSuccess )
	{
		
		goto ReleaseBuffer;
		
	}
	
	command->setBuffer 			( buffer );
	command->setByteCount 		( sizeof ( ATASMARTDataThresholds ) );
	command->setFeatures 		( kFeaturesRegisterReadDataThresholds );
	command->setOpcode			( kATAFnExecIO );
	command->setTimeoutMS		( kATAThirtySecondTimeoutInMS );
	command->setCylLo			( kSMARTMagicCylinderLoValue );
	command->setCylHi			( kSMARTMagicCylinderHiValue );
	command->setCommand			( kATAcmdSMART );
	command->setFlags 			( mATAFlagIORead );
	
	status = SendSMARTCommand ( command );
	if ( status == kIOReturnIOError )
	{
		
		if ( command->getEndErrorReg ( ) & 0x04 )
		{
			
			ERROR_LOG ( ( "ReadDataThresholds unsupported\n" ) );
			status = kIOReturnUnsupported;
			
		}
		
		if ( command->getEndErrorReg ( ) & 0x10 )
		{
			
			ERROR_LOG ( ( "ReadDataThresholds Not readable\n" ) );
			status = kIOReturnNotReadable;
			
		}
		
	}
	
	buffer->complete ( );
	
	
ReleaseBuffer:
	
	
	buffer->release ( );
	buffer = NULL;
	
	
ReleaseCommand:
	
	
	DeallocateCommand ( command );
	command = NULL;
	
	
ReleaseProvider:
	
	
	fProvider->release ( );
	
	
ErrorExit:
	
	
	fOutstandingCommands--;
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ ReadLogAtAddress - Reads the SMART log at the specified address. [PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
ATASMARTUserClient::ReadLogAtAddress ( 	ATASMARTReadLogStruct *	readLogData,
										UInt32					inStructSize )
{
	
	IOReturn				status 			= kIOReturnSuccess;
	IOATACommand *			command			= NULL;
	IOMemoryDescriptor *	buffer			= NULL;
	
	STATUS_LOG ( ( "ReadLogAtAddress called\n" ) );
	
	if ( inStructSize != sizeof ( ATASMARTReadLogStruct ) )
		return kIOReturnBadArgument;

	fOutstandingCommands++;
	
	if ( isInactive ( ) )
	{
		
		status = kIOReturnNoDevice;
		goto ErrorExit;
		
	}
	
	fProvider->retain ( );
	
	command = AllocateCommand ( );
	if ( command == NULL )
	{
		
		status = kIOReturnNoResources;
		goto ReleaseProvider;
		
	}
	
	buffer = IOMemoryDescriptor::withAddress ( 	( vm_address_t ) readLogData->buffer,
												readLogData->bufferSize,
												kIODirectionIn,
												fTask );
	
	if ( buffer == NULL )
	{
		
		status = kIOReturnNoResources;
		goto ReleaseCommand;
		
	}
	
	status = buffer->prepare ( );
	if ( status != kIOReturnSuccess )
	{
		
		goto ReleaseBuffer;
		
	}
	
	command->setBuffer 			( buffer );
	command->setByteCount 		( readLogData->bufferSize );
	command->setFeatures 		( kFeaturesRegisterReadLogAtAddress );
	command->setOpcode			( kATAFnExecIO );
	command->setTimeoutMS		( kATAThirtySecondTimeoutInMS );
	command->setSectorCount		( readLogData->numSectors );
	command->setSectorNumber	( readLogData->logAddress );
	command->setCylLo			( kSMARTMagicCylinderLoValue );
	command->setCylHi			( kSMARTMagicCylinderHiValue );
	command->setCommand			( kATAcmdSMART );
	command->setFlags 			( mATAFlagIORead );
	
	status = SendSMARTCommand ( command );
	if ( status == kIOReturnIOError )
	{
		
		if ( command->getEndErrorReg ( ) & 0x04 )
		{
			
			ERROR_LOG ( ( "ReadLogAtAddress %d unsupported\n", readLogData->logAddress ) );
			status = kIOReturnUnsupported;
			
		}
			
		if ( command->getEndErrorReg ( ) & 0x10 )
		{
			
			ERROR_LOG ( ( "ReadLogAtAddress %d unreadable\n", readLogData->logAddress ) );
			status = kIOReturnNotReadable;
			
		}
		
	}
	
	buffer->complete ( );
	
	
ReleaseBuffer:
	
	
	buffer->release ( );
	buffer = NULL;
	
	
ReleaseCommand:
	
	
	DeallocateCommand ( command );
	command = NULL;
	
	
ReleaseProvider:
	
	
	fProvider->release ( );
	
	
ErrorExit:
	
	
	fOutstandingCommands--;
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ WriteLogAtAddress - Writes the SMART log at the specified address. [PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
ATASMARTUserClient::WriteLogAtAddress ( ATASMARTWriteLogStruct *	writeLogData,
										UInt32						inStructSize )
{
	
	IOReturn				status 			= kIOReturnSuccess;
	IOATACommand *			command			= NULL;
	IOMemoryDescriptor *	buffer			= NULL;
	
	STATUS_LOG ( ( "WriteLogAtAddress called\n" ) );
	
	if ( inStructSize != sizeof ( ATASMARTWriteLogStruct ) )
		return kIOReturnBadArgument;
	
	fOutstandingCommands++;
	
	if ( isInactive ( ) )
	{
		
		status = kIOReturnNoDevice;
		goto ErrorExit;
		
	}
	
	fProvider->retain ( );
	
	command = AllocateCommand ( );
	if ( command == NULL )
	{
		
		status = kIOReturnNoResources;
		goto ReleaseProvider;
		
	}
	
	buffer = IOMemoryDescriptor::withAddress ( 	( vm_address_t ) writeLogData->buffer,
												writeLogData->bufferSize,
												kIODirectionOut,
												fTask );
	
	if ( buffer == NULL )
	{
		
		status = kIOReturnNoResources;
		goto ReleaseCommand;
		
	}
	
	status = buffer->prepare ( );
	if ( status != kIOReturnSuccess )
	{
		
		goto ReleaseBuffer;
		
	}
	
	command->setBuffer 			( buffer );
	command->setByteCount 		( writeLogData->bufferSize );
	command->setFeatures 		( kFeaturesRegisterWriteLogAtAddress );
	command->setOpcode			( kATAFnExecIO );
	command->setTimeoutMS		( kATAThirtySecondTimeoutInMS );
	command->setSectorCount		( writeLogData->numSectors );
	command->setSectorNumber	( writeLogData->logAddress );
	command->setCylLo			( kSMARTMagicCylinderLoValue );
	command->setCylHi			( kSMARTMagicCylinderHiValue );
	command->setCommand			( kATAcmdSMART );
	command->setFlags 			( mATAFlagIOWrite ); 
	
	status = SendSMARTCommand ( command );
	if ( status == kIOReturnIOError )
	{
		
		if ( command->getEndErrorReg ( ) & 0x04 )
		{
			
			ERROR_LOG ( ( "ReadLogAtAddress %d unsupported\n", writeLogData->logAddress ) );
			status = kIOReturnUnsupported;
			
		}
			
		if ( command->getEndErrorReg ( ) & 0x10 )
		{
			
			ERROR_LOG ( ( "ReadLogAtAddress %d unwriteable\n", writeLogData->logAddress ) );
			status = kIOReturnNotWritable;
			
		}
		
	}
	
	buffer->complete ( );
	
	
ReleaseBuffer:
	
	
	buffer->release ( );
	buffer = NULL;
	
	
ReleaseCommand:
	
	
	DeallocateCommand ( command );
	command = NULL;
	
	
ReleaseProvider:
	
	
	fProvider->release ( );
	
	
ErrorExit:
	
	
	fOutstandingCommands--;
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ GetIdentifyData - Gets identify data.							   [PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
ATASMARTUserClient::GetIdentifyData ( ATAGetIdentifyDataStruct *	identifyData,
									  UInt32 *						bytesTransferred,
									  UInt32						inStructSize,
									  UInt32 *						outStructSize )
{
	
	IOReturn					status 			= kIOReturnSuccess;
	IOATACommand *				command			= NULL;
	IOMemoryDescriptor *		userBuffer		= NULL;
	IOBufferMemoryDescriptor *	buffer			= NULL;
	UInt8 *						identifyDataPtr	= NULL;
	
	STATUS_LOG ( ( "GetIdentifyData called\n" ) );
	
	if ( inStructSize != sizeof ( ATAGetIdentifyDataStruct ) )
		return kIOReturnBadArgument;
	
	if ( *outStructSize != sizeof ( UInt32 ) )
		return kIOReturnBadArgument;
	
	*outStructSize		= 0;
	*bytesTransferred	= 0;
	
	fOutstandingCommands++;
	
	if ( isInactive ( ) )
	{
		
		status = kIOReturnNoDevice;
		goto ErrorExit;
		
	}
	
	fProvider->retain ( );
	
	command = AllocateCommand ( );
	if ( command == NULL )
	{
		
		status = kIOReturnNoResources;
		goto ReleaseProvider;
		
	}
	
	buffer = IOBufferMemoryDescriptor::withCapacity ( kATADefaultSectorSize, kIODirectionIn, false );
	if ( buffer == NULL )
	{
		
		status = kIOReturnNoResources;
		goto ReleaseCommand;
		
	}
	
	identifyDataPtr = ( UInt8 * ) buffer->getBytesNoCopy ( );
	
	status = buffer->prepare ( );
	if ( status != kIOReturnSuccess )
	{
		
		goto ReleaseBuffer;
		
	}

	userBuffer = IOMemoryDescriptor::withAddress ( ( vm_address_t ) identifyData->buffer,
												   identifyData->bufferSize,
												   kIODirectionIn,
												   fTask );
	
	if ( userBuffer == NULL )
	{
		
		status = kIOReturnNoResources;
		goto ReleaseBufferPrepared;
		 
	}
	
	status = userBuffer->prepare ( );
	if ( status != kIOReturnSuccess )
	{
		
		goto ReleaseUserBuffer;
		
	}
	
	command->setBuffer 				( buffer );
	command->setByteCount 			( kATADefaultSectorSize );
	command->setTransferChunkSize 	( kATADefaultSectorSize );
	command->setOpcode				( kATAFnExecIO );
	command->setTimeoutMS			( kATAThirtySecondTimeoutInMS );
	command->setCommand				( kATAcmdDriveIdentify );
	command->setFlags 				( mATAFlagIORead ); 
	command->setRegMask				( ( ataRegMask ) ( mATAErrFeaturesValid | mATAStatusCmdValid ) );
	
	status = SendSMARTCommand ( command );
	if ( status == kIOReturnSuccess )
	{

		UInt8 *		bufferToCopy = identifyDataPtr;
		
		#if defined(__BIG_ENDIAN__)
		
		// The identify device info needs to be byte-swapped on big-endian (ppc) 
		// systems becuase 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.			
		
		IOByteCount		index;
		UInt8			temp;
		UInt8 *			firstBytePtr;
		
		*bytesTransferred = command->getActualTransfer ( );
		
		for ( index = 0; index < buffer->getLength ( ); index += 2 )
		{
			
			firstBytePtr 		= identifyDataPtr;		// save pointer
			temp 				= *identifyDataPtr++;	// Save Byte0, point to Byte1
			*firstBytePtr 		= *identifyDataPtr;		// Byte0 = Byte1
			*identifyDataPtr++	= temp;					// Byte1 = Byte0
			
		}
		
		#endif
		
		userBuffer->writeBytes ( 0, bufferToCopy, userBuffer->getLength ( ) );
		
		*outStructSize = sizeof ( UInt32 );
		
	}
	
	userBuffer->complete ( );
	
	
ReleaseUserBuffer:
	
	userBuffer->release ( );
	userBuffer = NULL;
	
	
ReleaseBufferPrepared:
	
	buffer->complete ( );
	
	
ReleaseBuffer:
	
	
	buffer->release ( );
	buffer = NULL;
	
	
ReleaseCommand:
	
	
	DeallocateCommand ( command );
	command = NULL;
	
	
ReleaseProvider:
	
	
	fProvider->release ( );
	
	
ErrorExit:
	
	
	fOutstandingCommands--;
	
	return status;
	
}


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


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ getTargetAndMethodForIndex - 	Returns a pointer to the target of the
//									method call and the method vector itself
//									based on the provided index.	[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOExternalMethod *
ATASMARTUserClient::getTargetAndMethodForIndex ( IOService ** target, UInt32 index )
{
	
	if ( index >= kIOATASMARTMethodCount )
		return NULL;
	
	if ( isInactive ( ) )
		return NULL;
	
	*target = this;
	
	return &sMethods[index];
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ GatedWaitForCommand -	Waits for signal to wake up. It must hold the
//							workloop lock in order to call commandSleep()
//																	[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
ATASMARTUserClient::GatedWaitForCommand ( IOATACommand * command )
{
	
	IOReturn			status = kIOReturnSuccess;
	ATASMARTRefCon *	refCon;
	
	STATUS_LOG ( ( "GatedWaitForCommand called\n" ) );
	
	refCon = ( ATASMARTRefCon * ) command->refCon;
		
	// Check to make sure the command hasn't been completed and called the
	// callback handler before the stack unwinds and we get the workloop
	// lock. This usually won't happen since the callback runs on the
	// secondary interrupt context (involves going through the scheduler),
	// but it is still good to do this sanity check.
	while ( refCon->isDone != true )
	{
		
		fCommandGate->commandSleep ( &refCon->sleepOnIt, THREAD_UNINT );
		
	}
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ HandleTerminate -	Handles terminating our object and any resources it
//						allocated.									[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
ATASMARTUserClient::HandleTerminate ( IOService * provider )
{
	
	IOReturn	status = kIOReturnSuccess;
	
	STATUS_LOG ( ( "HandleTerminate called\n" ) );

	while ( fOutstandingCommands != 0 )
	{
		IOSleep ( 10 );
	}

	// Check if we have our provider open.	
	if ( provider->isOpen ( this ) )
	{
		
		// Yes we do, so close the connection
		STATUS_LOG ( ( "Closing provider\n" ) );
		provider->close ( this, kIOATASMARTUserClientAccessMask );
		
	}
	
	// Decouple us from the IORegistry.
	detach ( provider );
	fProvider = NULL;
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ SendSMARTCommand - Sends a command to the hardware synchronously.
//						 This is a helper method for all our non-exclusive
//						 methods.									[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
ATASMARTUserClient::SendSMARTCommand ( IOATACommand * command )
{
	
	ATASMARTRefCon		refCon;
	IOReturn			status 	= kIOReturnSuccess;
	
	bzero ( &refCon, sizeof ( refCon ) );
	
	refCon.isDone 	= false;
	refCon.self		= this;
	
	STATUS_LOG ( ( "SendSMARTCommand called\n" ) );
	
	command->setCallbackPtr ( &ATASMARTUserClient::sCommandCallback );
	command->refCon = ( void * ) &refCon;
	
	// Retain this object. It will be released by CommandCallback method.
	retain ( );
	
	status = fProvider->sendSMARTCommand ( command );
	if ( status == kIOReturnSuccess )
	{
		
		fCommandGate->runAction ( ( IOCommandGate::Action ) &ATASMARTUserClient::sWaitForCommand,
							  ( void * ) command );
		
		STATUS_LOG ( ( "SendSMARTCommand runAction done\n" ) );
		
		switch ( command->getResult ( ) )
		{
			
			case kATANoErr:
				status = kIOReturnSuccess;
				break;
				
			case kATATimeoutErr:
				status = kIOReturnTimeout;
				break;
				
			default:
				status = kIOReturnIOError;			
				break;
			
		}
				
	}
	
	else
	{
		
		// Error path, release this object since we called retain()
		release ( );
		
	}
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ CommandCallback - 	This method is called by sCommandCallback as the
//							completion routine for all IOATACommands.
//																	[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

void
ATASMARTUserClient::CommandCallback ( IOATACommand * command )
{
	
	ATASMARTRefCon *		refCon	= NULL;
		
	refCon = ( ATASMARTRefCon * ) command->refCon;
	fCommandGate->commandWakeup ( &refCon->sleepOnIt, true );
	release ( );
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ AllocateCommand - 	This method allocates an IOATACommand object.
//																	[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOATACommand *
ATASMARTUserClient::AllocateCommand ( void )
{
	
	IOATACommand *	command = NULL;
	IOATADevice *	device	= NULL;
	
	STATUS_LOG ( ( "AllocateCommand called\n" ) );
	
	device = ( IOATADevice * ) ( fProvider->getProvider ( )->getProvider ( ) );
	command = device->allocCommand ( );
	
	return command;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ DeallocateCommand - 	This method deallocates an IOATACommand object.
//																	[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

void
ATASMARTUserClient::DeallocateCommand ( IOATACommand * command )
{
	
	STATUS_LOG ( ( "DeallocateCommand called\n" ) );
	
	if ( command != NULL )
	{
		
		IOATADevice *	device	= NULL;
		
		device = ( IOATADevice * ) ( fProvider->getProvider ( )->getProvider ( ) );
		device->freeCommand ( command );
		
	}
	
}


#if 0
#pragma mark -
#pragma mark Static Methods
#pragma mark -
#endif


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ sWaitForCommand - Called by runAction and holds the workloop lock.
//																	[STATIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
ATASMARTUserClient::sWaitForCommand ( void * 			userClient,
									  IOATACommand * 	command )
{
	
	return ( ( ATASMARTUserClient * ) userClient )->GatedWaitForCommand ( command );
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ sCommandCallback -	Static completion routine. Calls CommandCallback.
//							It holds the workloop lock as well since it is on
//							the completion chain from the controller.
//																	[STATIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

void
ATASMARTUserClient::sCommandCallback ( IOATACommand * command )
{
	
	ATASMARTRefCon *		refCon	= NULL;
	
	STATUS_LOG ( ( "ATASMARTUserClient::sCommandCallback called.\n") );
	
	refCon = ( ATASMARTRefCon * ) command->refCon;
	if ( refCon == NULL )
	{
		
		ERROR_LOG ( ( "ATASMARTUserClient::sCommandCallback refCon == NULL.\n" ) );
		PANIC_NOW ( ( "ATASMARTUserClient::sCommandCallback refCon == NULL." ) );
		return;
		
	}
	
	refCon->isDone = true;
	refCon->self->CommandCallback ( command );
	
}