/*
 * Copyright (c) 2001 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 "SCSITaskUserClient.h"
#include "SCSITaskLib.h"

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

// IOKit storage includes
#include <IOKit/storage/IOBlockStorageDriver.h>

// SCSI Architecture Model Family includes
#include <IOKit/scsi-commands/IOSCSIPrimaryCommandsDevice.h>
#include <IOKit/scsi-commands/IOSCSIMultimediaCommandsDevice.h>
#include <IOKit/scsi-commands/SCSICmds_REQUEST_SENSE_Defs.h>
#include <IOKit/scsi-commands/SCSICmds_INQUIRY_Definitions.h>
#include <IOKit/scsi-commands/SCSICommandOperationCodes.h>


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

#define DEBUG 									0
#define DEBUG_ASSERT_COMPONENT_NAME_STRING		"SCSITaskUserClient"

#if DEBUG
#define SCSI_TASK_USER_CLIENT_DEBUGGING_LEVEL	0
#endif

#include "IOSCSIArchitectureModelFamilyDebugging.h"


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

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

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


#define super IOUserClient
OSDefineMetaClassAndStructors ( SCSITaskUserClient, IOUserClient );


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


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

IOExternalMethod
SCSITaskUserClient::sMethods[kSCSITaskUserClientMethodCount] =
{
	{
		// Method #0 IsExclusiveAccessAvailable
		0,
		( IOMethod ) &SCSITaskUserClient::IsExclusiveAccessAvailable,
		kIOUCScalarIScalarO,
		0,
		0
	},
	{
		// Method #1 ObtainExclusiveAccess
		0,
		( IOMethod ) &SCSITaskUserClient::ObtainExclusiveAccess,
		kIOUCScalarIScalarO,
		0,
		0
	},
	{
		// Method #2 ReleaseExclusiveAccess
		0,
		( IOMethod ) &SCSITaskUserClient::ReleaseExclusiveAccess,
		kIOUCScalarIScalarO,
		0,
		0
	},
	{
		// Method #3 CreateTask
		0,
		( IOMethod ) &SCSITaskUserClient::CreateTask,
		kIOUCScalarIScalarO,
		0,
		1
	},
	{
		// Method #4 ReleaseTask
		0,
		( IOMethod ) &SCSITaskUserClient::ReleaseTask,
		kIOUCScalarIScalarO,
		1,
		0
	},
	{
		// Method #5 AbortTask
		0,
		( IOMethod ) &SCSITaskUserClient::AbortTask,
		kIOUCScalarIScalarO,
		1,
		0
	},
	{
		// Method #6 ExecuteTask
		0,
		( IOMethod ) &SCSITaskUserClient::ExecuteTask,
		kIOUCScalarIStructI,
		0,
		0xFFFFFFFF
	},
	{
		// Method #7 SetBuffers
		0,
		( IOMethod ) &SCSITaskUserClient::SetBuffers,
		kIOUCScalarIScalarO,
		4,
		0
	},
	{
		// Method #8 Inquiry
		0,
		( IOMethod ) &SCSITaskUserClient::Inquiry,
		kIOUCStructIStructO,
		sizeof ( AppleInquiryStruct ),
		sizeof ( SCSITaskStatus )
	},
	{
		// Method #9 TestUnitReady
		0,
		( IOMethod ) &SCSITaskUserClient::TestUnitReady,
		kIOUCScalarIStructO,
		1,
		sizeof ( SCSITaskStatus )
	},
	{
		// Method #10 GetPerformance
		0,
		( IOMethod ) &SCSITaskUserClient::GetPerformance,
		kIOUCStructIStructO,
		sizeof ( AppleGetPerformanceStruct ),
		sizeof ( SCSITaskStatus )
	},
	{
		// Method #11 GetConfiguration
		0,
		( IOMethod ) &SCSITaskUserClient::GetConfiguration,
		kIOUCStructIStructO,
		sizeof ( AppleGetConfigurationStruct ),
		sizeof ( SCSITaskStatus )
	},
	{
		// Method #12 ModeSense10
		0,
		( IOMethod ) &SCSITaskUserClient::ModeSense10,
		kIOUCStructIStructO,
		sizeof ( AppleModeSense10Struct ),
		sizeof ( SCSITaskStatus )
	},
	{
		// Method #13 SetWriteParametersModePage
		0,
		( IOMethod ) &SCSITaskUserClient::SetWriteParametersModePage,
		kIOUCStructIStructO,
		sizeof ( AppleWriteParametersModePageStruct ),
		sizeof ( SCSITaskStatus )
	},
	{
		// Method #14 GetTrayState
		0,
		( IOMethod ) &SCSITaskUserClient::GetTrayState,
		kIOUCScalarIScalarO,
		0,
		1
	},
	{
		// Method #15 SetTrayState
		0,
		( IOMethod ) &SCSITaskUserClient::SetTrayState,
		kIOUCScalarIScalarO,
		1,
		0
	},
	{
		// Method #16 ReadTableOfContents
		0,
		( IOMethod ) &SCSITaskUserClient::ReadTableOfContents,
		kIOUCStructIStructO,
		sizeof ( AppleReadTableOfContentsStruct ),
		sizeof ( SCSITaskStatus )
	},
	{
		// Method #17 ReadDiscInformation
		0,
		( IOMethod ) &SCSITaskUserClient::ReadDiscInformation,
		kIOUCStructIStructO,
		sizeof ( AppleReadDiscInfoStruct ),
		sizeof ( SCSITaskStatus )
	},
	{
		// Method #18 ReadTrackInformation
		0,
		( IOMethod ) &SCSITaskUserClient::ReadTrackInformation,
		kIOUCStructIStructO,
		sizeof ( AppleReadTrackInfoStruct ),
		sizeof ( SCSITaskStatus )
	},
	{
		// Method #19 ReadDVDStructure
		0,
		( IOMethod ) &SCSITaskUserClient::ReadDVDStructure,
		kIOUCStructIStructO,
		sizeof ( AppleReadDVDStructureStruct ),
		sizeof ( SCSITaskStatus )
	}
};


IOExternalAsyncMethod SCSITaskUserClient::sAsyncMethods[kSCSITaskUserClientAsyncMethodCount] = 
{
    {   //  Async Method #0  SetAsyncCallback
        0,
        ( IOAsyncMethod ) &SCSITaskUserClient::SetAsyncCallback,
        kIOUCScalarIScalarO,
        3,
        0
    }
};


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


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

bool
SCSITaskUserClient::initWithTask ( task_t owningTask,
								   void * securityToken,
								   UInt32 type )
{
	
	bool	result	= false;
	
	STATUS_LOG ( ( "SCSITaskUserClient::initWithTask called\n" ) );
	
	result = super::initWithTask ( owningTask, securityToken, type );
	require ( result, BAD_CONNECTION_TYPE );
	require_action ( ( type == kSCSITaskLibConnection ), BAD_CONNECTION_TYPE, result = false );
	
	fTask = owningTask;
	result = true;
	
	
BAD_CONNECTION_TYPE:
	
	
	return result;
	
}


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

bool
SCSITaskUserClient::start ( IOService * provider )
{
	
	bool			result		= false;
	OSIterator *	iterator	= NULL;
	OSObject *		object		= NULL;
	IOWorkLoop *	workLoop	= NULL;
	
	STATUS_LOG ( ( "SCSITaskUserClient::start\n" ) );
	
	require ( ( fProvider == 0 ), GENERAL_ERR );
	require ( super::start ( provider ), GENERAL_ERR );
	
	// Zero our array for tasks.
	bzero ( fArray, sizeof ( SCSITask * ) * kMaxSCSITaskArraySize );
	
	// Save the provider
	fProvider = provider;
	
	// See if this object exports the IOSCSIProtocolInterface
	fProtocolInterface = OSDynamicCast ( IOSCSIProtocolInterface, provider );
	if ( fProtocolInterface == NULL )
	{
		
		ERROR_LOG ( ( "Provider not IOSCSIProtocolInterface\n" ) );
		
		// This provider doesn't have the interface we need, so walk the
		// parents until we get one which does (usually only one object)
		
		iterator = provider->getParentIterator ( gIOServicePlane );
		if ( iterator != NULL )
		{
			
			STATUS_LOG ( ( "Got parent iterator\n" ) );
			
			while ( object = iterator->getNextObject ( ) )
			{
				
				// Is it the interface we want?
				fProtocolInterface = OSDynamicCast ( IOSCSIProtocolInterface,
													 ( IOService * ) object );
				
				STATUS_LOG ( ( "%s candidate IOSCSIProtocolInterface\n", ( ( IOService * ) object )->getName ( ) ) );
				
				if ( fProtocolInterface != NULL )
				{
					
					STATUS_LOG ( ( "Found IOSCSIProtocolInterface\n" ) );
					break;
				
				}
				
			}
			
			// release the iterator
			iterator->release ( );
			
			// Did we find one?
			require_nonzero ( fProtocolInterface, GENERAL_ERR );
			
		}
		
	}
	
	STATUS_LOG ( ( "Creating command gate\n" ) );
	fCommandGate = IOCommandGate::commandGate ( this );
	require_nonzero ( fCommandGate, GENERAL_ERR );
	
	workLoop = getWorkLoop ( );
	require_nonzero_action ( workLoop,
							 GENERAL_ERR,
							 fCommandGate->release ( ) );
	
	workLoop->addEventSource ( fCommandGate );
	
	result = fProvider->open ( this, kIOSCSITaskUserClientAccessMask, 0 );
	require_action ( result,
					 GENERAL_ERR,
					 workLoop->removeEventSource ( fCommandGate );
					 fCommandGate->release ( );
					 fCommandGate = NULL );
	
	
GENERAL_ERR:
	
	
	return result;
	
}


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

IOReturn
SCSITaskUserClient::clientClose ( void )
{
	
	require_nonzero ( fProvider, GENERAL_ERR );
	HandleTerminate ( fProvider );
	
	
GENERAL_ERR:
	
	
	return super::clientClose ( );
	
}


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

void
SCSITaskUserClient::free ( void )
{
	
	if ( fCommandGate != NULL )
	{
		
		fCommandGate->release ( );
		fCommandGate = NULL;
		
	}
	
	super::free ( );
	
}


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

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


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ IsExclusiveAccessAvailable - 	Determines if exclusive acces is available
//									for this client.				[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
SCSITaskUserClient::IsExclusiveAccessAvailable ( void )
{
	
	IOReturn	status = kIOReturnNoDevice;
	bool		state = true;
	
	STATUS_LOG ( ( "IsExclusiveAccessAvailable called\n" ) );
	
	require ( isInactive ( ) == false, GENERAL_ERR );
	
	// First get the state. If there is no exclusive client, then
	// we attempt to set the state (which may fail but shouldn't
	// under normal circumstances).
	state = fProtocolInterface->GetUserClientExclusivityState ( );
	require_action ( ( state == false ), GENERAL_ERR, status = kIOReturnExclusiveAccess );
	
	STATUS_LOG ( ( "No current exclusive client\n" ) );
		
	// Ok. There is no exclusive client.
	status = kIOReturnSuccess;
	
	
GENERAL_ERR:
	
	
	STATUS_LOG ( ( "IsExclusiveAccessAvailable: status = %d\n", status ) );
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ ObtainExclusiveAccess - 	Obtains exclusive access for this client.
//																	[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
SCSITaskUserClient::ObtainExclusiveAccess ( void )
{
	
	IOReturn	status	= kIOReturnNoDevice;
	bool		state 	= true;
	
	STATUS_LOG ( ( "ObtainExclusiveAccess called\n" ) );
	
	require ( isInactive ( ) == false, GENERAL_ERR );
	
	// First get the state. If there is no exclusive client, then
	// we attempt to set the state (which may fail but shouldn't
	// under normal circumstances).
	state = fProtocolInterface->GetUserClientExclusivityState ( );
	require_action ( ( state == false ), GENERAL_ERR, status = kIOReturnExclusiveAccess );
		
	STATUS_LOG ( ( "No current exclusive client\n" ) );
	
	// Ok. There is no exclusive client. Try to become the
	// exclusive client. This would only fail if two clients
	// are competing for the same exclusive access at the exact
	// same time.
	status = fProtocolInterface->SetUserClientExclusivityState ( this, true );
	require_success ( status, GENERAL_ERR );
	
	if ( fProtocolInterface->IsPowerManagementIntialized ( ) )
	{
		
		UInt32	minutes = 0;
		
		// Make sure the idle timer for the in-kernel driver is set to
		// never change power state.
		fProtocolInterface->getAggressiveness ( kPMMinutesToSpinDown, &minutes );
		fProtocolInterface->setAggressiveness ( kPMMinutesToSpinDown, minutes );
		
	}
	
	
GENERAL_ERR:
	
	
	STATUS_LOG ( ( "ObtainExclusiveAccess: status = %d\n", status ) );
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ ReleaseExclusiveAccess - 	Releases exclusive access for this client.
//																	[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
SCSITaskUserClient::ReleaseExclusiveAccess ( void )
{
	
	IOReturn	status	= kIOReturnNoDevice;
	bool		state	= true;
	
	STATUS_LOG ( ( "ReleaseExclusiveAccess called\n" ) );
	
	require ( isInactive ( ) == false, GENERAL_ERR );
	
	// Get the user client state. We don't need to release exclusive
	// access if we don't have exclusive access (just to prevent
	// faulty user-land code from doing something bad).
	state = fProtocolInterface->GetUserClientExclusivityState ( );
	require_action_quiet ( state, GENERAL_ERR, status = kIOReturnNotPermitted );
	
	STATUS_LOG ( ( "There is a current exclusive client\n" ) );
	
	// Release our exclusive connection.
	status = fProtocolInterface->SetUserClientExclusivityState ( this, false );
	require_success ( status, GENERAL_ERR );
	
	if ( fProtocolInterface->IsPowerManagementIntialized ( ) )
	{
		
		UInt32	minutes = 0;
		
		// Make sure the idle timer for the in-kernel driver is set to
		// what the setting is now.
		fProtocolInterface->getAggressiveness ( kPMMinutesToSpinDown, &minutes );
		fProtocolInterface->setAggressiveness ( kPMMinutesToSpinDown, minutes );
	
	}
	
	
GENERAL_ERR:
	
	
	STATUS_LOG ( ( "ReleaseExclusiveAccess: status = %d\n", status ) );
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ CreateTask - 	Creates a SCSITask.								   [PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
SCSITaskUserClient::CreateTask ( SInt32 * taskReference )
{
	
	IOReturn			status 	= kIOReturnNoDevice;
    SInt32				taskRef	= -1;
    SCSITask *			task	= NULL;
	SCSITaskRefCon *	refCon	= NULL;
	
	STATUS_LOG ( ( "CreateTask called\n" ) );
	
	require ( isInactive ( ) == false, GENERAL_ERR );
	
	check ( taskReference );
	
	task = new SCSITask;
	require_nonzero_action_string ( task,
									ALLOCATION_FAILED_ERR,
									status = kIOReturnNoResources,
									"task == NULL, memory allocation failed\n" );
	
	// Initialize the task.
	require_string ( task->ResetForNewTask ( ), INIT_FAILED_ERR,
					 "task->ResetForNewTask ( ) == false, memory allocation failed\n" );
	
	refCon = IONew ( SCSITaskRefCon, 1 );
	require_nonzero_string ( refCon, INIT_FAILED_ERR,
							 "refCon == NULL, memory allocation failed\n" );
	
	bzero ( refCon, sizeof ( SCSITaskRefCon ) );
	task->SetApplicationLayerReference ( refCon );
	
	// Run the action to add it to the array of tasks
	status = fCommandGate->runAction ( ( IOCommandGate::Action ) &SCSITaskUserClient::sCreateTask,
									   ( void * ) task,
									   ( void * ) &taskRef );
	
	require_success ( status, ACTION_ERR );
	
	STATUS_LOG ( ( "taskRef = %ld\n", taskRef ) );
	
	*taskReference = taskRef;
		
	return status;
	
	
ACTION_ERR:	
	
	
	require_nonzero ( refCon, INIT_FAILED_ERR );
	IODelete ( refCon, SCSITaskRefCon, 1 );
	refCon = NULL;
	
	
INIT_FAILED_ERR:
	
	
	require_nonzero ( task, ALLOCATION_FAILED_ERR );
	task->release ( );
	task = NULL;
	
	
ALLOCATION_FAILED_ERR:
GENERAL_ERR:
	
		
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ ReleaseTask - Releases a SCSITask.							[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
SCSITaskUserClient::ReleaseTask ( SInt32 taskReference )
{
	
	IOReturn				status 	= kIOReturnBadArgument;
	SCSITask *				task	= NULL;
	IOMemoryDescriptor *	buffer	= NULL;
	SCSITaskRefCon *		refCon	= NULL;
	
	STATUS_LOG ( ( "SCSITaskUserClient::ReleaseTask\n" ) );

	require_action ( isInactive ( ) == false, GENERAL_ERR, status = kIOReturnNoDevice );
	
	require ( ( taskReference >= 0 ) && ( taskReference < kMaxSCSITaskArraySize ),
			  GENERAL_ERR );
	
	status = fCommandGate->runAction ( ( IOCommandGate::Action ) &SCSITaskUserClient::sReleaseTask,
									   ( void * ) taskReference,
									   ( void * ) &task );
	
	require_success ( status, GENERAL_ERR );
	
	refCon = ( SCSITaskRefCon * ) task->GetApplicationLayerReference ( );
	if ( refCon != NULL )
	{
		
		buffer = refCon->taskResultsBuffer;
		if ( buffer != NULL )
		{
			
			buffer->release ( );
			
		}
		
		IODelete ( refCon, SCSITaskRefCon, 1 );
		task->SetApplicationLayerReference ( NULL );
		
	}
	
	task->release ( );
	
	
GENERAL_ERR:
	
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ ExecuteTask - Executes a task passed in from user space.		[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
SCSITaskUserClient::ExecuteTask ( SCSITaskData * args, UInt32 argSize )
{
	
	SCSITask *				request				= NULL;
	SCSITaskRefCon *		refCon				= NULL;
	IOReturn				status				= kIOReturnBadArgument;
	bool					userBufPrepared 	= false;
	bool					resultsBufPrepared 	= false;
	bool					senseBufPrepared 	= false;
	IOMemoryDescriptor *	buffer				= NULL;
	
	STATUS_LOG ( ( "SCSITaskUserClient::ExecuteTask called\n" ) );
	STATUS_LOG ( ( "argSize = %ld\n", argSize ) );
	
	fOutstandingCommands++;
	
	require_action ( isInactive ( ) == false, GENERAL_ERR, status = kIOReturnNoDevice );
	
	check ( args );
	
	require ( ( args->taskReference >= 0 ) && ( args->taskReference < kMaxSCSITaskArraySize ),
			  GENERAL_ERR );
	
	request = fArray[args->taskReference];
	require_nonzero ( request, GENERAL_ERR );
	
	nrequire_action ( request->IsTaskActive ( ), GENERAL_ERR, status = kIOReturnNotPermitted );
	
	refCon = ( SCSITaskRefCon * ) request->GetApplicationLayerReference ( );
	
	refCon->commandType = args->isSync ? kCommandTypeExecuteSync : kCommandTypeExecuteAsync;
	refCon->self		= this;
	
	request->ResetForNewTask ( );
	
	request->SetApplicationLayerReference ( ( void * ) refCon );
	
	status = fCommandGate->runAction ( ( IOCommandGate::Action ) &SCSITaskUserClient::sValidateTask,
									   ( void * ) request,
									   ( void * ) args,
									   ( void * ) argSize );
	
	require_success ( status, ACTION_FAILED_ERR );
	
	STATUS_LOG ( ( "Task is valid\n" ) );
	
	request->SetTimeoutDuration ( args->timeoutDuration );
	
	if ( ( args->scatterGatherEntries > 0 ) && ( args->requestedTransferCount > 0 ) )
	{
		
		IODirection		ioDirection;
		
		STATUS_LOG ( ( "Preparing buffers\n" ) );
		
		ioDirection = ( args->transferDirection == kSCSIDataTransfer_FromTargetToInitiator ) ? kIODirectionIn : kIODirectionOut;
		
		buffer = IOMemoryDescriptor::withRanges ( args->scatterGatherList,
												  args->scatterGatherEntries,
												  ioDirection,
												  fTask );
		
		require_nonzero_action_string ( buffer,
										BUFFER_CREATE_FAILED_ERR,
										status = kIOReturnNoResources,
										"Error creating memory descriptor\n" );
		
		status = buffer->prepare ( );
		require_success_string ( status,
								 BUFFER_PREPARE_FAILED_ERR,
								 "Error preparing user memory descriptor\n" );
		
		userBufPrepared = true;
		
		request->SetDataBuffer ( buffer );
		request->SetRequestedDataTransferCount ( args->requestedTransferCount );
		
	}
	
	status = refCon->taskResultsBuffer->prepare ( );
	require_success_string ( status,
							 BUFFER_PREPARE_FAILED_ERR,
							 "Error preparing results memory descriptor\n" );
	
	resultsBufPrepared = true;
	
	status = request->GetAutosenseDataBuffer ( )->prepare ( );
	require_success_string ( status,
							 BUFFER_PREPARE_FAILED_ERR,
							 "Error preparing sense data memory descriptor\n" );
	
	senseBufPrepared = true;
	
	// Retain the task, results buffer, and sense data buffer. They will be released by sTaskCallback method.
	request->retain ( );
	refCon->taskResultsBuffer->retain ( );
	
	request->SetTaskCompletionCallback ( &SCSITaskUserClient::sTaskCallback );
	request->SetAutosenseCommand ( kSCSICmd_REQUEST_SENSE, 0x00, 0x00, 0x00, sizeof ( SCSI_Sense_Data ), 0x00 );
	fProtocolInterface->ExecuteCommand ( request );
	
	if ( refCon->commandType == kCommandTypeExecuteSync )
	{
		
		retain ( );
		
		status = fCommandGate->runAction ( 	( IOCommandGate::Action ) &SCSITaskUserClient::sWaitForTask,
									   		( void * ) request );
		
		if ( buffer != NULL )
		{
			
			// Make sure to complete any data buffers from client
			status = CompleteBuffers ( buffer );
		
		}
		
		release ( );
		
	}
	
	
	return status;
	
	
BUFFER_PREPARE_FAILED_ERR:
	
	
	if ( userBufPrepared )
		CompleteBuffers ( buffer );
		
	if ( resultsBufPrepared )
		refCon->taskResultsBuffer->complete ( );
		
	if ( senseBufPrepared )
		request->GetAutosenseDataBuffer ( )->complete ( );
	
	
BUFFER_CREATE_FAILED_ERR:	
ACTION_FAILED_ERR:
GENERAL_ERR:
	
	
	fOutstandingCommands--;
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ AbortTask - Aborts a task passed in from user space (if possible).
//																	[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
SCSITaskUserClient::AbortTask ( SInt32 taskReference )
{
	
	SCSITask * 	task	= NULL;
	IOReturn	status	= kIOReturnBadArgument;
	
	STATUS_LOG ( ( "SCSITaskUserClient::AbortTask called\n" ) );

	require_action ( isInactive ( ) == false, GENERAL_ERR, status = kIOReturnNoDevice );	
	require ( ( taskReference >= 0 ) && ( taskReference < kMaxSCSITaskArraySize ), GENERAL_ERR );
	
	task = fArray[taskReference];
	require_nonzero ( task, GENERAL_ERR );
	
	// Can't abort an inactive task
	require_action ( task->IsTaskActive ( ), GENERAL_ERR, status = kIOReturnNotPermitted );
	
	// Make sure we can send the command
	require_nonzero_action ( fProtocolInterface, GENERAL_ERR, status = kIOReturnNoDevice );
	
	fProtocolInterface->AbortCommand ( task );
	
	
GENERAL_ERR:
	
	
	return kIOReturnSuccess;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ SetAsyncCallback - 	Sets an async callback reference so a method is
//							called for asynchronous notifications.
//																	[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
SCSITaskUserClient::SetAsyncCallback ( OSAsyncReference	asyncRef,
									   SInt32			taskReference,
									   void *			callback,
									   void *			userRefCon )
{
	
	SCSITask *			task		= NULL;
	mach_port_t			wakePort 	= MACH_PORT_NULL;
	SCSITaskRefCon *	refCon		= NULL;
	IOReturn			status		= kIOReturnBadArgument;
	
	check ( callback );
	check ( userRefCon );
	
	STATUS_LOG ( ( "SCSITaskUserClient::SetAsyncCallback called\n" ) );
	
	require_action ( isInactive ( ) == false, GENERAL_ERR, status = kIOReturnNoDevice );
	require ( ( taskReference >= 0 ) && ( taskReference < kMaxSCSITaskArraySize ), GENERAL_ERR );
	
	task = fArray[taskReference];
	require_nonzero ( task, GENERAL_ERR );
	
	// Can't touch an active task
	nrequire_action ( task->IsTaskActive ( ), GENERAL_ERR, status = kIOReturnNotPermitted );
	
	STATUS_LOG ( ( "asyncRef[0] = %d\n", asyncRef[0] ) );
	
	wakePort = ( mach_port_t ) asyncRef[0];
	
	super::setAsyncReference ( asyncRef, wakePort, callback, userRefCon );
	
	refCon = ( SCSITaskRefCon * ) task->GetApplicationLayerReference ( );
	require_nonzero_action ( refCon, GENERAL_ERR, status = kIOReturnError );
	
	bcopy ( asyncRef, refCon->asyncReference, kOSAsyncRefSize );
	
	STATUS_LOG ( ( "refCon->asyncReference[0] = %d\n", refCon->asyncReference[0] ) );
	status = kIOReturnSuccess;
	
	
GENERAL_ERR:
	
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ SetBuffers - 	Sets the results buffer and sense data buffer for the
//					specified task.									[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
SCSITaskUserClient::SetBuffers ( SInt32			taskReference,
								 vm_address_t	results,
								 vm_address_t	senseData,
								 UInt32			senseBufferSize )
{
	
	IOReturn				status	= kIOReturnBadArgument;
	SCSITask *				task	= NULL;
	SCSITaskRefCon *		refCon	= NULL;
	IOMemoryDescriptor *	buffer	= NULL;
	bool					result	= false;
	
	STATUS_LOG ( ( "SCSITaskUserClient::SetBuffers called\n" ) );
	
	require_action ( isInactive ( ) == false, GENERAL_ERR, status = kIOReturnNoDevice );	
	require ( ( taskReference >= 0 ), GENERAL_ERR );
	require ( ( taskReference < kMaxSCSITaskArraySize ), GENERAL_ERR );
	
	task = fArray[taskReference];
	require ( task, GENERAL_ERR );
	
	// Can't touch an active task
	require_action ( ( task->IsTaskActive ( ) == false ),
					 GENERAL_ERR,
					 status = kIOReturnNotPermitted );
	
	refCon = ( SCSITaskRefCon * ) task->GetApplicationLayerReference ( );
	require_nonzero_action ( refCon, GENERAL_ERR, status = kIOReturnError );
	
	buffer = IOMemoryDescriptor::withAddress ( results,
											   sizeof ( SCSITaskResults ),
											   kIODirectionIn,
											   fTask );
	
	require_nonzero_action ( buffer, GENERAL_ERR, status = kIOReturnNoResources );
	
	if ( refCon->taskResultsBuffer != NULL )
	{
		
		refCon->taskResultsBuffer->release ( );
		
	}
	
	refCon->taskResultsBuffer = buffer;
	
	result = task->SetAutoSenseDataBuffer ( ( SCSI_Sense_Data * ) senseData,
											senseBufferSize,
											fTask );
	
	require_action ( result, GENERAL_ERR, status = kIOReturnNoResources );
	
	status = kIOReturnSuccess;
	
	
GENERAL_ERR:
	
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ Inquiry - Issues an INQUIRY command to the drive as defined by SPC-2.	
//																	[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
SCSITaskUserClient::Inquiry ( AppleInquiryStruct * 	inquiryData,
							  SCSITaskStatus * 		taskStatus,
							  UInt32				inStructSize,
							  UInt32 *				outStructSize )
{
	
	IOReturn				status 	= kIOReturnExclusiveAccess;
	IOReturn				status2 = kIOReturnExclusiveAccess;
	SCSITask * 				task 	= NULL;
	IOMemoryDescriptor *	buffer	= NULL;
	bool					state 	= true;
	
	check ( inquiryData );
	check ( taskStatus );
	check ( outStructSize );
	
	*outStructSize = 0;
	
	fOutstandingCommands++;
	
	require_action ( isInactive ( ) == false, GENERAL_ERR, status = kIOReturnNoDevice );
	
	state = fProtocolInterface->GetUserClientExclusivityState ( );
	nrequire ( state, EXCLUSIVE_ACCESS_ERR );
	
	// Create and initialize a task
	status = SetupTask ( &task );
	require_success ( status, TASK_SETUP_ERR );
	
	// Prepare the buffers
	status = PrepareBuffers ( &buffer, inquiryData->buffer, inquiryData->bufferSize, kIODirectionIn );
	require_success ( status, BUFFER_PREPARATION_ERR );
	
	// Prepare the task
	task->SetCommandDescriptorBlock ( kSCSICmd_INQUIRY,
									  0x00,
									  0x00,
									  0x00,
									  inquiryData->bufferSize,
									  0x00 );
	
	task->SetTimeoutDuration ( kTenSecondTimeoutInMS );
	task->SetDataTransferDirection ( kSCSIDataTransfer_FromTargetToInitiator );
	task->SetDataBuffer ( buffer );
	task->SetRequestedDataTransferCount ( inquiryData->bufferSize );
	
	status	= SendCommand ( task, inquiryData->senseDataBuffer, taskStatus );
	status2	= CompleteBuffers ( buffer );
	
	if ( ( status == kIOReturnSuccess ) && ( status2 != kIOReturnSuccess ) )
	{
		status = status2;
	}
	
	*outStructSize = sizeof ( SCSITaskStatus );
	
	
BUFFER_PREPARATION_ERR:
	
	
	task->release ( );
	
	
TASK_SETUP_ERR:
EXCLUSIVE_ACCESS_ERR:
GENERAL_ERR:
	
	
	fOutstandingCommands--;
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ TestUnitReady - 	Issues a TEST_UNIT_READY command to the drive as
//						defined by SPC-2.							[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
SCSITaskUserClient::TestUnitReady ( vm_address_t 		senseDataBuffer,
									SCSITaskStatus * 	taskStatus,
									UInt32 *			outStructSize )
{
	
	IOReturn				status 	= kIOReturnExclusiveAccess;
	SCSITask * 				task 	= NULL;
	bool					state 	= true;
	
	check ( taskStatus );
	check ( outStructSize );
	
	*outStructSize = 0;
	
	fOutstandingCommands++;
	
	require_action ( isInactive ( ) == false, GENERAL_ERR, status = kIOReturnNoDevice );
	
	state = fProtocolInterface->GetUserClientExclusivityState ( );
	nrequire ( state, EXCLUSIVE_ACCESS_ERR );
	
	// Create and initialize a task
	status = SetupTask ( &task );
	require_success ( status, TASK_SETUP_ERR );
	
	task->SetCommandDescriptorBlock ( kSCSICmd_TEST_UNIT_READY,
									  0x00,
									  0x00,
									  0x00,
									  0x00,
									  0x00 );
	
	task->SetTimeoutDuration ( kTenSecondTimeoutInMS );
	task->SetDataTransferDirection ( kSCSIDataTransfer_NoDataTransfer );
	
	status = SendCommand ( task, ( void * ) senseDataBuffer, taskStatus );
	
	task->release ( );
	*outStructSize = sizeof ( SCSITaskStatus );
	
	
TASK_SETUP_ERR:
EXCLUSIVE_ACCESS_ERR:
GENERAL_ERR:
	
	
	fOutstandingCommands--;
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ GetPerformance - 	Issues a GET_PERFORMANCE command to the drive as
//						defined by Mt. Fuji/SFF-8090i revision 5.0.	[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
SCSITaskUserClient::GetPerformance ( AppleGetPerformanceStruct * 	performanceData,
									 SCSITaskStatus * 				taskStatus,
									 UInt32 						inStructSize,
									 UInt32 * 						outStructSize )
{
	
	IOReturn				status 	= kIOReturnExclusiveAccess;
	IOReturn				status2 = kIOReturnExclusiveAccess;
	SCSITask * 				task 	= NULL;
	IOMemoryDescriptor *	buffer	= NULL;
	bool					state 	= true;
	
	check ( performanceData );
	check ( taskStatus );
	check ( outStructSize );
	
	*outStructSize = 0;
	
	fOutstandingCommands++;
	
	require_action ( isInactive ( ) == false, GENERAL_ERR, status = kIOReturnNoDevice );
	
	state = fProtocolInterface->GetUserClientExclusivityState ( );
	nrequire ( state, EXCLUSIVE_ACCESS_ERR );
	
	// Create and initialize a task
	status = SetupTask ( &task );
	require_success ( status, TASK_SETUP_ERR );
	
	// Prepare the buffers
	status = PrepareBuffers ( &buffer, performanceData->buffer, performanceData->bufferSize, kIODirectionIn );
	require_success ( status, BUFFER_PREPARATION_ERR );	
	
	task->SetCommandDescriptorBlock ( kSCSICmd_GET_PERFORMANCE,
									  performanceData->DATA_TYPE,
									  ( performanceData->STARTING_LBA >> 24 ) 	& 0xFF,
									  ( performanceData->STARTING_LBA >> 16 ) 	& 0xFF,
									  ( performanceData->STARTING_LBA >>  8 ) 	& 0xFF,
									  performanceData->STARTING_LBA         	& 0xFF,
									  0x00,
									  0x00,
									  ( performanceData->MAXIMUM_NUMBER_OF_DESCRIPTORS >> 8 )	& 0xFF,
									    performanceData->MAXIMUM_NUMBER_OF_DESCRIPTORS			& 0xFF,
									  0x00,
									  0x00 );
	
	task->SetTimeoutDuration ( kThirtySecondTimeoutInMS );
	task->SetDataTransferDirection ( kSCSIDataTransfer_FromTargetToInitiator );
	task->SetDataBuffer ( buffer );
	task->SetRequestedDataTransferCount ( performanceData->bufferSize );
	
	status	= SendCommand ( task, performanceData->senseDataBuffer, taskStatus );
	status2	= CompleteBuffers ( buffer );
	
	if ( ( status == kIOReturnSuccess ) && ( status2 != kIOReturnSuccess ) )
	{
		status = status2;
	}
	
	*outStructSize = sizeof ( SCSITaskStatus );
	
	
BUFFER_PREPARATION_ERR:
	
	
	task->release ( );
	
	
TASK_SETUP_ERR:
EXCLUSIVE_ACCESS_ERR:
GENERAL_ERR:
	
	
	fOutstandingCommands--;
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ GetConfiguration - 	Issues a GET_CONFIGURATION command to the drive as
//							defined by MMC-2.						[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
SCSITaskUserClient::GetConfiguration ( AppleGetConfigurationStruct * 	configData,
									   SCSITaskStatus * 				taskStatus,
									   UInt32 							inStructSize,
									   UInt32 * 						outStructSize )
{
	
	IOReturn				status 	= kIOReturnExclusiveAccess;
	IOReturn				status2 = kIOReturnExclusiveAccess;
	SCSITask * 				task 	= NULL;
	IOMemoryDescriptor *	buffer	= NULL;
	bool					state 	= true;
	
	check ( configData );
	check ( taskStatus );
	check ( outStructSize );
	
	*outStructSize = 0;
	
	fOutstandingCommands++;
	
	require_action ( isInactive ( ) == false, GENERAL_ERR, status = kIOReturnNoDevice );
	
	state = fProtocolInterface->GetUserClientExclusivityState ( );
	nrequire ( state, EXCLUSIVE_ACCESS_ERR );
	
	// Create and initialize a task
	status = SetupTask ( &task );
	require_success ( status, TASK_SETUP_ERR );
	
	// Prepare the buffers
	status = PrepareBuffers ( &buffer, configData->buffer, configData->bufferSize, kIODirectionIn );
	require_success ( status, BUFFER_PREPARATION_ERR );
	
	task->SetCommandDescriptorBlock ( 	kSCSICmd_GET_CONFIGURATION,
										configData->RT,
										( configData->STARTING_FEATURE_NUMBER >> 8 ) & 0xFF,
										  configData->STARTING_FEATURE_NUMBER        & 0xFF,
										0x00,
										0x00,
										0x00,
										( configData->bufferSize >> 8 ) 	& 0xFF,
										  configData->bufferSize        	& 0xFF,
										0x00 );
	
	task->SetTimeoutDuration ( kThirtySecondTimeoutInMS );
	task->SetDataTransferDirection ( kSCSIDataTransfer_FromTargetToInitiator );
	task->SetDataBuffer ( buffer );
	task->SetRequestedDataTransferCount ( configData->bufferSize );
	
	status	= SendCommand ( task, configData->senseDataBuffer, taskStatus );
	status2	= CompleteBuffers ( buffer );
	
	if ( ( status == kIOReturnSuccess ) && ( status2 != kIOReturnSuccess ) )
	{
		status = status2;
	}
	
	*outStructSize = sizeof ( SCSITaskStatus );
	
	
BUFFER_PREPARATION_ERR:
	
	
	task->release ( );
	
	
TASK_SETUP_ERR:
EXCLUSIVE_ACCESS_ERR:
GENERAL_ERR:
	
	
	fOutstandingCommands--;
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ ModeSense10 - Issues a MODE_SENSE_10 command to the drive as
//					defined by SPC-2.								[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
SCSITaskUserClient::ModeSense10 ( AppleModeSense10Struct * 	modeSenseData,
								  SCSITaskStatus * 			taskStatus,
								  UInt32 					inStructSize,
								  UInt32 * 					outStructSize )
{
	
	IOReturn				status 	= kIOReturnExclusiveAccess;
	IOReturn				status2 = kIOReturnExclusiveAccess;
	SCSITask * 				task 	= NULL;
	IOMemoryDescriptor *	buffer	= NULL;
	bool					state 	= true;
	
	check ( modeSenseData );
	check ( taskStatus );
	check ( outStructSize );
	
	*outStructSize = 0;
	
	fOutstandingCommands++;
	
	require_action ( isInactive ( ) == false, GENERAL_ERR, status = kIOReturnNoDevice );
	
	state = fProtocolInterface->GetUserClientExclusivityState ( );
	nrequire ( state, EXCLUSIVE_ACCESS_ERR );
	
	// Create and initialize a task
	status = SetupTask ( &task );
	require_success ( status, TASK_SETUP_ERR );
	
	// Prepare the buffers
	status = PrepareBuffers ( &buffer, modeSenseData->buffer, modeSenseData->bufferSize, kIODirectionIn );
	require_success ( status, BUFFER_PREPARATION_ERR );
	
	task->SetCommandDescriptorBlock ( kSCSICmd_MODE_SENSE_10,
									  ( modeSenseData->LLBAA << 4 ) | ( modeSenseData->DBD << 3 ),
									  ( modeSenseData->PC << 6 ) | modeSenseData->PAGE_CODE,
									  0x00,
									  0x00,
									  0x00,
									  0x00,
									  ( modeSenseData->bufferSize >> 8 & 0xFF ),
									  ( modeSenseData->bufferSize & 0xFF ),
									  0x00 );
	
	task->SetTimeoutDuration ( kThirtySecondTimeoutInMS );
	task->SetDataTransferDirection ( kSCSIDataTransfer_FromTargetToInitiator );
	task->SetDataBuffer ( buffer );
	task->SetRequestedDataTransferCount ( modeSenseData->bufferSize );
	
	status	= SendCommand ( task, modeSenseData->senseDataBuffer, taskStatus );	
	status2	= CompleteBuffers ( buffer );
	
	if ( ( status == kIOReturnSuccess ) && ( status2 != kIOReturnSuccess ) )
	{
		status = status2;
	}
	
	*outStructSize = sizeof ( SCSITaskStatus );
	
	
BUFFER_PREPARATION_ERR:
	
	
	task->release ( );
	
	
TASK_SETUP_ERR:
EXCLUSIVE_ACCESS_ERR:
GENERAL_ERR:
	
	
	fOutstandingCommands--;
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ SetWriteParametersModePage - 	Issues a MODE_SELECT_10 command to the drive
//									with the Write Parameters Mode Page set as
//									defined by MMC-2.				[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
SCSITaskUserClient::SetWriteParametersModePage ( AppleWriteParametersModePageStruct * paramsData,
												 SCSITaskStatus * 	taskStatus,
												 UInt32 			inStructSize,
												 UInt32 * 			outStructSize )
{
	
	IOReturn				status 	= kIOReturnExclusiveAccess;
	IOReturn				status2 = kIOReturnExclusiveAccess;
	SCSITask * 				task 	= NULL;
	IOMemoryDescriptor *	buffer	= NULL;
	bool					state 	= true;
	SCSICmdField1Bit		PF 		= 1;

	check ( paramsData );
	check ( taskStatus );
	check ( outStructSize );
	
	*outStructSize = 0;
	
	fOutstandingCommands++;
	
	require_action ( isInactive ( ) == false, GENERAL_ERR, status = kIOReturnNoDevice );
	
	state = fProtocolInterface->GetUserClientExclusivityState ( );
	nrequire ( state, EXCLUSIVE_ACCESS_ERR );
	
	// Create and initialize a task
	status = SetupTask ( &task );
	require_success ( status, TASK_SETUP_ERR );
	
	// Prepare the buffers
	status = PrepareBuffers ( &buffer, paramsData->buffer, paramsData->bufferSize, kIODirectionOut );
	require_success ( status, BUFFER_PREPARATION_ERR );
	
	task->SetCommandDescriptorBlock (	kSCSICmd_MODE_SELECT_10,
										( PF << 4),
										0x00,
										0x00,
										0x00,
										0x00,
										0x00,
										( paramsData->bufferSize >> 8 ) & 0xFF,
										  paramsData->bufferSize 		& 0xFF,
										0x00 );
	
	task->SetTimeoutDuration ( kThirtySecondTimeoutInMS );
	task->SetDataTransferDirection ( kSCSIDataTransfer_FromInitiatorToTarget );
	task->SetDataBuffer ( buffer );
	task->SetRequestedDataTransferCount ( paramsData->bufferSize );
	
	status	= SendCommand ( task, paramsData->senseDataBuffer, taskStatus );
	status2	= CompleteBuffers ( buffer );
	
	if ( ( status == kIOReturnSuccess ) && ( status2 != kIOReturnSuccess ) )
	{
		status = status2;
	}
	
	*outStructSize = sizeof ( SCSITaskStatus );
	
	
BUFFER_PREPARATION_ERR:
	
	
	task->release ( );
	
	
TASK_SETUP_ERR:
EXCLUSIVE_ACCESS_ERR:
GENERAL_ERR:
	
	
	fOutstandingCommands--;
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ GetTrayState - Returns the current tray state of the drive.	[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
SCSITaskUserClient::GetTrayState ( UInt32 * trayState )
{
	
	IOReturn		status 			= kIOReturnExclusiveAccess;
	UInt8			actualTrayState	= 0;
	bool			state			= false;
	
	fOutstandingCommands++;
	
	require_action ( isInactive ( ) == false, GENERAL_ERR, status = kIOReturnNoDevice );
	
	state = fProtocolInterface->GetUserClientExclusivityState ( );
	nrequire ( state, EXCLUSIVE_ACCESS_ERR );
	
	status = ( ( IOSCSIMultimediaCommandsDevice * ) fProtocolInterface )->GetTrayState ( &actualTrayState );
	require_success ( status, COMMAND_FAILED_ERR );
	*trayState = actualTrayState;
	
	
COMMAND_FAILED_ERR:
EXCLUSIVE_ACCESS_ERR:
GENERAL_ERR:
	
	
	fOutstandingCommands--;
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ SetTrayState - Sets the driveΥs tray state to the proposed state.
//																	[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
SCSITaskUserClient::SetTrayState ( UInt32 trayState )
{
	
	IOReturn		status 				= kIOReturnExclusiveAccess;
	UInt8			desiredTrayState	= 0;
	bool			state				= false;
	
	fOutstandingCommands++;
	
	require_action ( isInactive ( ) == false, GENERAL_ERR, status = kIOReturnNoDevice );
	
	state = fProtocolInterface->GetUserClientExclusivityState ( );
	nrequire ( state, EXCLUSIVE_ACCESS_ERR );
	
	desiredTrayState = trayState & kMMCDeviceTrayMask;
	status = ( ( IOSCSIMultimediaCommandsDevice * ) fProtocolInterface )->SetTrayState ( desiredTrayState );
	
	
EXCLUSIVE_ACCESS_ERR:
GENERAL_ERR:
	
	
	fOutstandingCommands--;
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ ReadTableOfContents - Issues a READ_TOC_PMA_ATIP command to the drive as
//							defined in MMC-2.						[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
SCSITaskUserClient::ReadTableOfContents ( AppleReadTableOfContentsStruct * 	readTOCData,
										  SCSITaskStatus * 					taskStatus,
										  UInt32 							inStructSize,
										  UInt32 * 							outStructSize )
{
	
	IOReturn				status 	= kIOReturnExclusiveAccess;
	IOReturn				status2	= kIOReturnExclusiveAccess;
	SCSITask * 				task 	= NULL;
	IOMemoryDescriptor *	buffer	= NULL;
	bool					state 	= true;
	
	check ( readTOCData );
	check ( taskStatus );
	check ( outStructSize );
	
	fOutstandingCommands++;
	
	require_action ( isInactive ( ) == false, GENERAL_ERR, status = kIOReturnNoDevice );
	
	state = fProtocolInterface->GetUserClientExclusivityState ( );
	nrequire ( state, EXCLUSIVE_ACCESS_ERR );
	
	// Create and initialize a task
	status = SetupTask ( &task );
	require_success ( status, TASK_SETUP_ERR );
	
	// Prepare the buffers
	status = PrepareBuffers ( &buffer, readTOCData->buffer, readTOCData->bufferSize, kIODirectionIn );
	require_success ( status, BUFFER_PREPARATION_ERR );
	
	if ( readTOCData->FORMAT & 0x04 )
	{
		
		// Use new style from MMC-2
		task->SetCommandDescriptorBlock ( kSCSICmd_READ_TOC_PMA_ATIP,
										  readTOCData->MSF << 1,
										  readTOCData->FORMAT,
										  0x00,
										  0x00,
										  0x00,
										  readTOCData->TRACK_SESSION_NUMBER,
										  ( readTOCData->bufferSize >> 8 ) & 0xFF,
										    readTOCData->bufferSize        & 0xFF,
										  0x00 );

	
	}
	
	else
	{

		// Use old style from SFF-8020i
		task->SetCommandDescriptorBlock ( kSCSICmd_READ_TOC_PMA_ATIP,
										  readTOCData->MSF << 1,
										  0x00,
										  0x00,
										  0x00,
										  0x00,
										  readTOCData->TRACK_SESSION_NUMBER,
										  ( readTOCData->bufferSize >> 8 ) & 0xFF,
										    readTOCData->bufferSize        & 0xFF,
										  ( readTOCData->FORMAT & 0x03 ) << 6 );
		
	}
	
	// set timeout to 30 seconds
	task->SetTimeoutDuration ( kThirtySecondTimeoutInMS );
	task->SetDataTransferDirection ( kSCSIDataTransfer_FromTargetToInitiator );
	task->SetDataBuffer ( buffer );
	task->SetRequestedDataTransferCount ( readTOCData->bufferSize );
	
	status	= SendCommand ( task, readTOCData->senseDataBuffer, taskStatus );
	status2 = CompleteBuffers ( buffer );
	
	if ( ( status == kIOReturnSuccess ) && ( status2 != kIOReturnSuccess ) )
	{
		status = status2;
	}
	
	*outStructSize = sizeof ( SCSITaskStatus );
	
	
BUFFER_PREPARATION_ERR:
	
	
	task->release ( );
	
	
TASK_SETUP_ERR:
EXCLUSIVE_ACCESS_ERR:
GENERAL_ERR:
	
	
	fOutstandingCommands--;
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ ReadDiscInformation - Issues a READ_DISC_INFORMATION command to the
//							drive as defined in MMC-2.				[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
SCSITaskUserClient::ReadDiscInformation ( AppleReadDiscInfoStruct * 	discInfoData,
										  SCSITaskStatus * 				taskStatus,
										  UInt32 						inStructSize,
										  UInt32 * 						outStructSize )
{
	
	IOReturn				status 	= kIOReturnExclusiveAccess;
	IOReturn				status2	= kIOReturnExclusiveAccess;
	SCSITask * 				task 	= NULL;
	IOMemoryDescriptor *	buffer	= NULL;
	bool					state 	= true;
		
	check ( discInfoData );
	check ( taskStatus );
	check ( outStructSize );
	
	fOutstandingCommands++;
	
	require_action ( isInactive ( ) == false, GENERAL_ERR, status = kIOReturnNoDevice );
	
	state = fProtocolInterface->GetUserClientExclusivityState ( );
	nrequire ( state, EXCLUSIVE_ACCESS_ERR );
	
	// Create and initialize a task
	status = SetupTask ( &task );
	require_success ( status, TASK_SETUP_ERR );
	
	// Prepare the buffers
	status = PrepareBuffers ( &buffer, discInfoData->buffer, discInfoData->bufferSize, kIODirectionIn );
	require_success ( status, BUFFER_PREPARATION_ERR );
	
	task->SetCommandDescriptorBlock ( kSCSICmd_READ_DISC_INFORMATION,
									  0x00,
									  0x00,
									  0x00,
									  0x00,
									  0x00,
									  0x00,
									  ( discInfoData->bufferSize >> 8 ) & 0xFF,
									    discInfoData->bufferSize        & 0xFF,
									  0x00 );
	
	// set timeout to 30 seconds
	task->SetTimeoutDuration ( kThirtySecondTimeoutInMS );
	task->SetDataTransferDirection ( kSCSIDataTransfer_FromTargetToInitiator );
	task->SetDataBuffer ( buffer );
	task->SetRequestedDataTransferCount ( discInfoData->bufferSize );
	
	status 	= SendCommand ( task, discInfoData->senseDataBuffer, taskStatus );
	status2 = CompleteBuffers ( buffer );
	
	if ( ( status == kIOReturnSuccess ) && ( status2 != kIOReturnSuccess ) )
	{
		status = status2;
	}
	
	*outStructSize = sizeof ( SCSITaskStatus );
	
	
BUFFER_PREPARATION_ERR:
	
	
	task->release ( );
	
	
TASK_SETUP_ERR:
EXCLUSIVE_ACCESS_ERR:
GENERAL_ERR:
	
	
	fOutstandingCommands--;
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ ReadTrackInformation - 	Issues a READ_TRACK/RZONE_INFORMATION command
//								to the drive as defined in MMC-2.	[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
SCSITaskUserClient::ReadTrackInformation ( AppleReadTrackInfoStruct * 	trackInfoData,
										   SCSITaskStatus * 			taskStatus,
										   UInt32 						inStructSize,
										   UInt32 * 					outStructSize )
{
	
	IOReturn				status 	= kIOReturnExclusiveAccess;
	IOReturn				status2 = kIOReturnExclusiveAccess;
	SCSITask * 				task 	= NULL;
	IOMemoryDescriptor *	buffer	= NULL;
	bool					state 	= true;
	
	check ( trackInfoData );
	check ( taskStatus );
	check ( outStructSize );
	
	fOutstandingCommands++;
	
	require_action ( isInactive ( ) == false, GENERAL_ERR, status = kIOReturnNoDevice );
	
	state = fProtocolInterface->GetUserClientExclusivityState ( );
	nrequire ( state, EXCLUSIVE_ACCESS_ERR );
	
	// Create and initialize a task
	status = SetupTask ( &task );
	require_success ( status, TASK_SETUP_ERR );
	
	// Prepare the buffers
	status = PrepareBuffers ( &buffer, trackInfoData->buffer, trackInfoData->bufferSize, kIODirectionIn );
	require_success ( status, BUFFER_PREPARATION_ERR );
	
	task->SetCommandDescriptorBlock ( kSCSICmd_READ_TRACK_INFORMATION,
									  trackInfoData->ADDRESS_NUMBER_TYPE & 0x03,
									  ( trackInfoData->LOGICAL_BLOCK_ADDRESS_TRACK_SESSION_NUMBER >> 24 ) & 0xFF,
									  ( trackInfoData->LOGICAL_BLOCK_ADDRESS_TRACK_SESSION_NUMBER >> 16 ) & 0xFF,
									  ( trackInfoData->LOGICAL_BLOCK_ADDRESS_TRACK_SESSION_NUMBER >>  8 ) & 0xFF,
								 	    trackInfoData->LOGICAL_BLOCK_ADDRESS_TRACK_SESSION_NUMBER         & 0xFF,
									  0x00,
									  ( trackInfoData->bufferSize >>  8 ) & 0xFF,
								  	    trackInfoData->bufferSize         & 0xFF,
									  0x00 );
	
	// set timeout to 30 seconds
	task->SetTimeoutDuration ( kThirtySecondTimeoutInMS );
	task->SetDataTransferDirection ( kSCSIDataTransfer_FromTargetToInitiator );
	task->SetDataBuffer ( buffer );
	task->SetRequestedDataTransferCount ( trackInfoData->bufferSize );
	
	status	= SendCommand ( task, trackInfoData->senseDataBuffer, taskStatus );
	status2	= CompleteBuffers ( buffer );
	
	if ( ( status == kIOReturnSuccess ) && ( status2 != kIOReturnSuccess ) )
	{
		status = status2;
	}
	
	*outStructSize = sizeof ( SCSITaskStatus );
	
	
BUFFER_PREPARATION_ERR:
	
	
	task->release ( );
	
	
TASK_SETUP_ERR:
EXCLUSIVE_ACCESS_ERR:
GENERAL_ERR:
	
	
	fOutstandingCommands--;
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ ReadDVDStructure - 	Issues a READ_DVD_STRUCTURE command to the drive as
//							defined in MMC-2.						[PUBLIC]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
SCSITaskUserClient::ReadDVDStructure ( AppleReadDVDStructureStruct * 	dvdStructData,
									   SCSITaskStatus * 				taskStatus,
									   UInt32 							inStructSize,
									   UInt32 * 						outStructSize )
{
	
	IOReturn				status 	= kIOReturnExclusiveAccess;
	IOReturn				status2 = kIOReturnExclusiveAccess;
	SCSITask * 				task 	= NULL;
	IOMemoryDescriptor *	buffer	= NULL;
	bool					state 	= true;
	
	check ( dvdStructData );
	check ( taskStatus );
	check ( outStructSize );
	
	fOutstandingCommands++;
	
	require_action ( isInactive ( ) == false, GENERAL_ERR, status = kIOReturnNoDevice );
	
	state = fProtocolInterface->GetUserClientExclusivityState ( );
	nrequire ( state, EXCLUSIVE_ACCESS_ERR );
	
	// Create and initialize a task
	status = SetupTask ( &task );
	require_success ( status, TASK_SETUP_ERR );
	
	// Prepare the buffers
	status = PrepareBuffers ( &buffer, dvdStructData->buffer, dvdStructData->bufferSize, kIODirectionIn );
	require_success ( status, BUFFER_PREPARATION_ERR );
	
	task->SetCommandDescriptorBlock ( kSCSICmd_READ_DVD_STRUCTURE,
									  0x00,
									  ( dvdStructData->ADDRESS >> 24 ) & 0xFF,
									  ( dvdStructData->ADDRESS >> 16 ) & 0xFF,
									  ( dvdStructData->ADDRESS >> 8  ) & 0xFF,
									    dvdStructData->ADDRESS		   & 0xFF,
									    dvdStructData->LAYER_NUMBER,
									    dvdStructData->FORMAT,
									  ( dvdStructData->bufferSize >> 8 ) & 0xFF,
									    dvdStructData->bufferSize        & 0xFF,
									  ( dvdStructData->AGID << 6 ),
									  0x00 );
	
	// set timeout to 30 seconds
	task->SetTimeoutDuration ( kThirtySecondTimeoutInMS );
	task->SetDataTransferDirection ( kSCSIDataTransfer_FromTargetToInitiator );
	task->SetDataBuffer ( buffer );
	task->SetRequestedDataTransferCount ( dvdStructData->bufferSize );
	
	status	= SendCommand ( task, dvdStructData->senseDataBuffer, taskStatus );
	status2	= CompleteBuffers ( buffer );
	
	if ( ( status == kIOReturnSuccess ) && ( status2 != kIOReturnSuccess ) )
	{
		status = status2;
	}
	
	*outStructSize = sizeof ( SCSITaskStatus );
	
	
BUFFER_PREPARATION_ERR:
	
	
	task->release ( );
	
	
TASK_SETUP_ERR:
EXCLUSIVE_ACCESS_ERR:
GENERAL_ERR:
	
	
	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 *
SCSITaskUserClient::getTargetAndMethodForIndex (
							IOService **	target,
							UInt32			index )
{
	
	IOExternalMethod *	method = NULL;
	
	check ( target );
	require ( index < kSCSITaskUserClientMethodCount, GENERAL_ERR );
	
	fOutstandingCommands++;
	
	require ( isInactive ( ) == false, GENERAL_ERR );
	
	fProtocolInterface->retain ( );
	
	if ( fProtocolInterface->IsPowerManagementIntialized ( ) )
	{
		fProtocolInterface->CheckPowerState ( );
	}
	
	fProtocolInterface->release ( );
	
	*target = this;
	method 	= &sMethods[index];
	
	
GENERAL_ERR:
	
	
	fOutstandingCommands--;
	
	return method;
	
}


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

IOExternalAsyncMethod *
SCSITaskUserClient::getAsyncTargetAndMethodForIndex (
							IOService **	target,
							UInt32			index )
{
	
	
	IOExternalAsyncMethod *	method = NULL;
	
	check ( target );
	require ( index < kSCSITaskUserClientAsyncMethodCount, GENERAL_ERR );
	
	fOutstandingCommands++;
	
	require ( isInactive ( ) == false, GENERAL_ERR );
	
	fProtocolInterface->retain ( );
	
	if ( fProtocolInterface->IsPowerManagementIntialized ( ) )
	{
		fProtocolInterface->CheckPowerState ( );
	}
	
	fProtocolInterface->release ( );
	
	*target = this;
	method 	= &sAsyncMethods[index];
	
	
GENERAL_ERR:
	
	
	fOutstandingCommands--;
	
	return method;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ GatedCreateTask -	Safely obtains a taskReference for the newly created
//						task if one is available. It is called while holding
//						the workloop lock.							[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
SCSITaskUserClient::GatedCreateTask ( SCSITask * task, SInt32 * taskReference )
{
	
	IOReturn		status 	= kIOReturnNoResources;
	UInt32			index	= 0;
	
	check ( task );
	check ( taskReference );
	
	for ( index = 0; index < kMaxSCSITaskArraySize; index++ )
	{
		
		if ( fArray[index] == NULL )
			break;
		
	}
	
	require_action ( index < kMaxSCSITaskArraySize, ARRAY_INDEX_ERR, *taskReference = -1 );
	
	fArray[index]	= task;
	*taskReference 	= index;
	status			= kIOReturnSuccess;
	
	
ARRAY_INDEX_ERR:	
	
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ GatedReleaseTask -	Safely obtains a taskReference for the newly
//							created task if one is available. It is called
//							while holding the workloop lock.		[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
SCSITaskUserClient::GatedReleaseTask ( SInt32 taskReference, SCSITask ** task )
{
	
	IOReturn	status 	= kIOReturnSuccess;
	SCSITask *	victim	= NULL;
	
	check ( task != NULL );
	
	// Sanity check
	victim = fArray[taskReference];
	require_nonzero_action ( victim, GENERAL_ERR, status = kIOReturnBadArgument );
	
	// If the task is still active, it cannot be released.
	require_action ( ( victim->IsTaskActive ( ) == false ), GENERAL_ERR, status = kIOReturnNotPermitted );
	
	// Remove it now.
	fArray[taskReference] = 0;
	
	STATUS_LOG ( ( "Removed object from array\n" ) );
	
	// Pass back the pointer to the object so it can be safely destroyed.
	*task = victim;	
	
	
GENERAL_ERR:
	
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ GatedValidateTask -	Safely validates a task. It is called while
//							holding the workloop lock.				[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
SCSITaskUserClient::GatedValidateTask ( SCSITask * 		request,
										SCSITaskData * 	args,
										UInt32 			argSize )
{
	
	IOReturn	status = kIOReturnBadArgument;
	
	STATUS_LOG ( ( "SCSITaskUserClient::GatedValidateTask called\n" ) );
	
	check ( request );
	check ( args );
	
	require ( ( args->taskAttribute >= kSCSITask_SIMPLE ) &&
			  ( args->taskAttribute <= kSCSITask_ACA ),
			  INVALID_ARGUMENT );
	
	request->SetTaskAttribute ( args->taskAttribute );
	
	#if ( SCSI_TASK_USER_CLIENT_DEBUGGING_LEVEL >= 3 )
	{
		
		SCSICommandDescriptorBlock	cdb;
		UInt8						commandLength;
		
		request->GetCommandDescriptorBlock ( &cdb );
		commandLength = request->GetCommandDescriptorBlockSize ( );
		
		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] ) );
			
		}
		
	}
	#endif /* ( SCSI_TASK_USER_CLIENT_DEBUGGING_LEVEL >= 3 ) */
	
	switch ( args->cdbSize )
	{
		
		case kSCSICDBSize_6Byte:
			request->SetCommandDescriptorBlock ( args->cdbData[0],
												 args->cdbData[1],
												 args->cdbData[2],
												 args->cdbData[3],
												 args->cdbData[4],
												 args->cdbData[5] );
			break;
		
		case kSCSICDBSize_10Byte:
			request->SetCommandDescriptorBlock ( args->cdbData[0],
												 args->cdbData[1],
												 args->cdbData[2],
												 args->cdbData[3],
												 args->cdbData[4],
												 args->cdbData[5],
												 args->cdbData[6],
												 args->cdbData[7],
												 args->cdbData[8],
												 args->cdbData[9] );
			break;
		
		case kSCSICDBSize_12Byte:
			request->SetCommandDescriptorBlock ( args->cdbData[0],
												 args->cdbData[1],
												 args->cdbData[2],
												 args->cdbData[3],
												 args->cdbData[4],
												 args->cdbData[5],
												 args->cdbData[6],
												 args->cdbData[7],
												 args->cdbData[8],
												 args->cdbData[9],
												 args->cdbData[10],
												 args->cdbData[11] );
			break;
		
		case kSCSICDBSize_16Byte:
			request->SetCommandDescriptorBlock ( args->cdbData[0],
												 args->cdbData[1],
												 args->cdbData[2],
												 args->cdbData[3],
												 args->cdbData[4],
												 args->cdbData[5],
												 args->cdbData[6],
												 args->cdbData[7],
												 args->cdbData[8],
												 args->cdbData[9],
												 args->cdbData[10],
												 args->cdbData[11],
												 args->cdbData[12],
												 args->cdbData[13],
												 args->cdbData[14],
												 args->cdbData[15] );
			break;
		
		default:
			ERROR_LOG ( ( "Invalid command length size\n" ) );
			goto INVALID_ARGUMENT;
		
	}
	
	require ( ( args->transferDirection <= kSCSIDataTransfer_FromTargetToInitiator ),
			  INVALID_ARGUMENT );
	
	request->SetDataTransferDirection ( args->transferDirection );
	
	if ( args->scatterGatherEntries > 0 )
	{
		
		UInt32		structSizeWithoutList = sizeof ( SCSITaskData ) - sizeof ( IOVirtualRange );
		UInt32		sgListSize = 0;
		
		sgListSize = argSize - structSizeWithoutList;
		
		require_string ( ( sgListSize / sizeof ( IOVirtualRange ) ) == args->scatterGatherEntries,
						 INVALID_ARGUMENT,
						 "Invalid scatter-gather list" );
		
	}
	
	status = kIOReturnSuccess;
	
	
INVALID_ARGUMENT:
	
	
	return status;
	
}


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

IOReturn
SCSITaskUserClient::GatedWaitForTask ( SCSITask * request )
{
	
	IOReturn			status	= kIOReturnSuccess;
	SCSITaskRefCon *	refCon	= NULL;
	
	check ( request );
	refCon = ( SCSITaskRefCon * ) request->GetApplicationLayerReference ( );
	check ( refCon );
	
	while ( request->GetTaskState ( ) != kSCSITaskState_ENDED )
	{
		
		status = fCommandGate->commandSleep ( &refCon->commandType, THREAD_UNINT );
		
	}
	
	status = kIOReturnSuccess;
	
	return status;
	
}


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

IOReturn
SCSITaskUserClient::HandleTerminate ( IOService * provider )
{
	
	IOReturn		status 		= kIOReturnSuccess;
	IOWorkLoop *	workLoop	= NULL;
	
	check ( provider );
	
	while ( fOutstandingCommands != 0 )
	{
		IOSleep ( 10 );
	}
	
	if ( provider->isOpen ( this ) )
	{
		
		STATUS_LOG ( ( "Closing provider\n" ) );
		provider->close ( this, kIOSCSITaskUserClientAccessMask );
		
	}
	
	detach ( provider );
	fProvider = NULL;
	
	ReleaseExclusiveAccess ( );
	
	for ( UInt32 index = 0; index < kMaxSCSITaskArraySize; index++ )
	{
		
		SCSITask *	task = NULL;
		
		task = ( SCSITask * ) fArray[index];
		if ( task == NULL )
			continue;
		
		ReleaseTask ( index );
		
	}
	
	workLoop = getWorkLoop ( );
	if ( workLoop != NULL )
		workLoop->removeEventSource ( fCommandGate );
	
	return status;
	
}


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

IOReturn
SCSITaskUserClient::SendCommand ( SCSITask * 		request,
								  void * 			senseBuffer,
								  SCSITaskStatus *	taskStatus )
{
	
	SCSITaskRefCon			refCon	= { 0 };
	IOReturn				status	= kIOReturnNoResources;
	bool					result  = false;
	
	check ( request );
	check ( senseBuffer );
	check ( taskStatus );
	
	*taskStatus = kSCSITaskStatus_No_Status;
	
	refCon.commandType 	= kCommandTypeNonExclusive;
	refCon.self			= this;
	
	request->SetTaskCompletionCallback ( &SCSITaskUserClient::sTaskCallback );
	request->SetApplicationLayerReference ( ( void * ) &refCon );
	request->SetAutosenseCommand ( kSCSICmd_REQUEST_SENSE,
								   0x00,
								   0x00,
								   0x00,
								   sizeof ( SCSI_Sense_Data ),
								   0x00 );
	
	result = request->SetAutoSenseDataBuffer (
								( SCSI_Sense_Data * ) senseBuffer,
								sizeof ( SCSI_Sense_Data ),
								fTask );
	
	require ( result, ErrorExit );
	
	status = request->GetAutosenseDataBuffer ( )->prepare ( );
	require_success ( status, ErrorExit );
	
	// Retain the task. It will be released by sTaskCallback method.
	request->retain ( );
	
	fProtocolInterface->ExecuteCommand ( request );
	fCommandGate->runAction ( ( IOCommandGate::Action ) &SCSITaskUserClient::sWaitForTask,
							  ( void * ) request );
	
	*taskStatus = request->GetTaskStatus ( );
	status = kIOReturnSuccess;
	
	
ErrorExit:
	
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ TaskCallback - 	This method is called by sTaskCallback as the
//						completion routine for all SCSITasks.
//																	[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

void
SCSITaskUserClient::TaskCallback ( SCSITask * task, SCSITaskRefCon * refCon )
{
	
	IOMemoryDescriptor *	buffer = NULL;
	
	STATUS_LOG ( ( "SCSITaskUserClient::TaskCallback called.\n") );
	
	check ( task );
	check ( refCon );
	
	buffer = refCon->taskResultsBuffer;	
	if ( buffer != NULL )
	{
		
		SCSITaskResults		results;
		UInt32				numBytes = 0;
		
		results.serviceResponse			= task->GetServiceResponse ( );
		results.taskStatus				= task->GetTaskStatus ( );
		results.realizedTransferCount	= task->GetRealizedDataTransferCount ( );
		
		numBytes = sizeof ( SCSITaskResults );
		
		buffer->writeBytes ( 0, ( void * ) &results, numBytes );
		buffer->complete ( );
		
		// Make sure to release since it was retained by ExecuteTask
		buffer->release ( );
		buffer = NULL;
		
	}
	
	buffer = task->GetAutosenseDataBuffer ( );
	if ( buffer != NULL )
	{
		buffer->complete ( );
	}
	
	// Release the task as it was retained in ExecuteTask or SendCommand
	task->release ( );
	
	if ( refCon->commandType == kCommandTypeNonExclusive )
	{
		
		fCommandGate->commandWakeup ( &refCon->commandType );
		
	}
	
	else if ( refCon->commandType == kCommandTypeExecuteSync )
	{
		
		// We've executed the task, so decrement the count now.
		fOutstandingCommands--;
		fCommandGate->commandWakeup ( &refCon->commandType );
		
	}
		
	else
	{
		
		OSAsyncReference	asyncRef;
		
		asyncRef = refCon->asyncReference;
		
		STATUS_LOG ( ( "asyncRef[0] = %d\n", asyncRef[0] ) );
		
		buffer = task->GetDataBuffer ( );
		if ( buffer != NULL )
		{
			
			// Make sure to complete any data buffers from client
			CompleteBuffers ( buffer );
			
		}
		
		// Send the result
        ( void ) sendAsyncResult ( asyncRef, kIOReturnSuccess, NULL, 0 );
		
		// We've executed asynchronously, so decrement the count now.
		fOutstandingCommands--;
		
	}
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ SetupTask - 	Creates and initializes a new SCSITask.			[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
SCSITaskUserClient::SetupTask ( SCSITask ** task )
{
	
	SCSITask *		newTask = NULL;
	IOReturn		status	= kIOReturnNoMemory;
	
	check ( task );
	
	newTask = new SCSITask;
	require_nonzero ( newTask, TASK_CREATE_ERR );
	require_action ( newTask->ResetForNewTask ( ),
					 TASK_CREATE_ERR,
					 newTask->release ( ) );
	
	*task 	= newTask;
	status	= kIOReturnSuccess;
	
	
TASK_CREATE_ERR:
	
	
	return status;	
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ PrepareBuffers - 	Prepares any user space buffers.			[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
SCSITaskUserClient::PrepareBuffers ( IOMemoryDescriptor **	buffer,
									 void *					userBuffer,
									 IOByteCount			bufferSize,
									 IODirection			direction )
{
	
	IOReturn	status = kIOReturnNoMemory;
	
	check ( buffer );
	check ( userBuffer );
	
	*buffer = IOMemoryDescriptor::withAddress ( ( vm_address_t ) userBuffer,
											    bufferSize,
											    direction,
											    fTask );
	
	require_nonzero ( *buffer, BUFFER_CREATE_ERR );
	
	status = ( *buffer )->prepare ( );
	if ( status != kIOReturnSuccess )
	{
		
		( *buffer )->release ( );
		( *buffer ) = NULL;
		
	}
	
	
BUFFER_CREATE_ERR:
	
	
	return status;
	
}


//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
//	₯ CompleteBuffers - Completes any user space buffers.			[PROTECTED]
//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ

IOReturn
SCSITaskUserClient::CompleteBuffers ( IOMemoryDescriptor * buffer )
{
	
	IOReturn	status = kIOReturnSuccess;
	
	check ( buffer );
	
	buffer->complete ( );
	buffer->release ( );
	
	return status;
	
}


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


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

IOReturn
SCSITaskUserClient::sCreateTask ( void *		self,
								  SCSITask *	task,
								  SInt32 *		taskReference )
{
	
	check ( self );
	return ( ( SCSITaskUserClient * ) self )->GatedCreateTask ( task, taskReference );
	
}


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

IOReturn
SCSITaskUserClient::sReleaseTask ( void * self, SInt32 taskReference, void * task )
{
	
	check ( self );
	return ( ( SCSITaskUserClient * ) self )->GatedReleaseTask ( taskReference,
																 ( SCSITask ** ) task );
	
}


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

IOReturn
SCSITaskUserClient::sWaitForTask ( void *		userClient,
								   SCSITask *	request )
{
	
	check ( userClient );
	return ( ( SCSITaskUserClient * ) userClient )->GatedWaitForTask ( request );
	
}


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

IOReturn
SCSITaskUserClient::sValidateTask ( void *			userClient,
									SCSITask *		request,
									SCSITaskData *	args,
									UInt32			argSize )
{
	
	check ( userClient );
	return ( ( SCSITaskUserClient * ) userClient )->GatedValidateTask ( request, args, argSize );
	
}


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

void
SCSITaskUserClient::sTaskCallback ( SCSITaskIdentifier completedTask )
{
	
	SCSITaskRefCon *		refCon	= NULL;
	SCSITask *				task	= NULL;
	
	STATUS_LOG ( ( "SCSITaskUserClient::sTaskCallback called.\n") );
	
	task = OSDynamicCast ( SCSITask, completedTask );
	require_nonzero_string ( task, GENERAL_ERR,
							 "SCSITaskUserClient::sTaskCallback task == NULL." );
	
	refCon = ( SCSITaskRefCon * ) task->GetApplicationLayerReference ( );
	require_nonzero_string ( task, GENERAL_ERR,
							 "SCSITaskUserClient::sTaskCallback refCon == NULL." );
	
	refCon->self->TaskCallback ( task, refCon );
	
	
GENERAL_ERR:
	
	
	return;
	
}