IOUSBInterfaceUserClient.cpp   [plain text]


/*
 * Copyright (c) 1998-2003 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
//================================================================================================
//
//   Headers
//
//================================================================================================
//
#include <IOKit/usb/IOUSBInterfaceUserClient.h>
#include <IOKit/IOKitKeys.h>

//================================================================================================
//
//   Local Definitions
//
//================================================================================================
//
#define super IOUserClient

#define fClientRunningUnderRosetta					fIOUSBInterfaceUserClientExpansionData->clientRunningUnderRosetta
#define fNeedContiguousMemoryForLowLatencyIsoch     fIOUSBInterfaceUserClientExpansionData->needContiguousMemoryForLowLatencyIsoch

#ifndef kIOUserClientCrossEndianKey
#define kIOUserClientCrossEndianKey "IOUserClientCrossEndian"
#endif

#ifndef kIOUserClientCrossEndianCompatibleKey
#define kIOUserClientCrossEndianCompatibleKey "IOUserClientCrossEndianCompatible"
#endif

//=============================================================================================
//
//	Note on the use of IncrementOutstandingIO(), DecrementOutstandingIO() and doing extra
//	retain()/release() on async calls:
//
//	The UserClient is a complex driver.  It is attached to its provider when it is instantiated
// 	due to a device/interface being created but it is ALSO used by the user land IOUSBLib.  
//	Thus, it effectively has 2 clients, one on the kernel side and one on the user side.  When
//	the user side closes the connection to this user client, we terminate ourselves.  When the
//	device is unplugged, we get terminated by our provider.  The Increment/DecrementOutstandingIO()
//	calls are used to keep track of any IO that has not finished so that when OUR provider attempts
//	to terminate us, we won't get released (because we have our provider open) until the IO completes.
//	However, when our user land client closes us, we terminate ourselves.  In this case the 
//	OutstandingIO() calls do not help us in preventing our object from going away.  That is why we
//	also use a retain()/release() pair on async calls;  if we are closed before the async request is
//	complete, we will not go away and hence won't panic when we try to execute the completion routine.
//
//	Yes, we could have use only retain()/release(), but all our other kernel drivers use the 
//	outstandingIO metaphor so I thought it would be complete to use it here as well.
//
//	Another interesting piece is that we wait 1 ms in our clientClose method.  This will allow other 
//	pending threads to run and possible enter our driver (See bug #2862199).
//
//=============================================================================================
//

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
OSDefineMetaClassAndStructors(IOUSBInterfaceUserClient, super)
OSDefineMetaClassAndStructors(IOUSBLowLatencyCommand, IOCommand)


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
enum {
        kMethodObjectThis = 0,
        kMethodObjectOwner
    };
    
const IOExternalMethod 
IOUSBInterfaceUserClient::sMethods[kNumUSBInterfaceMethods] = {
    { //    kUSBInterfaceUserClientOpen
	(IOService*)kMethodObjectThis,
	(IOMethod) &IOUSBInterfaceUserClient::open,
	kIOUCScalarIScalarO,
	1,
	0
    },
    { //    kUSBInterfaceUserClientClose
	(IOService*)kMethodObjectThis,
	(IOMethod) &IOUSBInterfaceUserClient::close,
	kIOUCScalarIScalarO,
	0,
	0
    },
    { //    kUSBInterfaceUserClientGetDevice
	(IOService*)kMethodObjectThis,
	(IOMethod) &IOUSBInterfaceUserClient::GetDevice,
	kIOUCScalarIScalarO,
	0,
	1
    },
    { //    kUSBInterfaceUserClientSetAlternateInterface
	(IOService*)kMethodObjectThis,
	(IOMethod) &IOUSBInterfaceUserClient::SetAlternateInterface,
	kIOUCScalarIScalarO,
	1,
	0
    },
    { //    kUSBInterfaceUserClientGetFrameNumber
	(IOService*)kMethodObjectThis,
	(IOMethod) &IOUSBInterfaceUserClient::GetFrameNumber,
	kIOUCScalarIStructO,
	0,
	0xffffffff
    },
    { //    kUSBInterfaceUserClientGetPipeProperties
	(IOService*)kMethodObjectThis,
	(IOMethod) &IOUSBInterfaceUserClient::GetPipeProperties,
	kIOUCScalarIScalarO,
	1,
	5
    },
    { //    kUSBInterfaceUserClientReadPipe
	(IOService*)kMethodObjectThis,
	(IOMethod) &IOUSBInterfaceUserClient::ReadPipe,
	kIOUCScalarIStructO,
	3,
	0xffffffff
    },
    { //    kUSBInterfaceUserClientReadPipeOOL
	(IOService*)kMethodObjectThis,
	(IOMethod) &IOUSBInterfaceUserClient::ReadPipeOOL,
	kIOUCStructIStructO,
	sizeof(IOUSBBulkPipeReq),
	sizeof(UInt32)
    },
    { //    kUSBInterfaceUserClientWritePipe
	(IOService*)kMethodObjectThis,
	(IOMethod) &IOUSBInterfaceUserClient::WritePipe,
	kIOUCScalarIStructI,
	3,
	0xffffffff
    },
    { //    kUSBInterfaceUserClientWritePipeOOL
	(IOService*)kMethodObjectThis,
	(IOMethod) &IOUSBInterfaceUserClient::WritePipeOOL,
	kIOUCStructIStructO,
	sizeof(IOUSBBulkPipeReq),
	0
    },
    { //    kUSBInterfaceUserClientGetPipeStatus
	(IOService*)kMethodObjectThis,
	(IOMethod) &IOUSBInterfaceUserClient::GetPipeStatus,
	kIOUCScalarIScalarO,
	1,
	0
    },
    { //    kUSBInterfaceUserClientAbortPipe
	(IOService*)kMethodObjectThis,
	(IOMethod) &IOUSBInterfaceUserClient::AbortPipe,
	kIOUCScalarIScalarO,
	1,
	0
    },
    { //    kUSBInterfaceUserClientResetPipe
	(IOService*)kMethodObjectThis,
	(IOMethod) &IOUSBInterfaceUserClient::ResetPipe,
	kIOUCScalarIScalarO,
	1,
	0
    },
    { //    kUSBInterfaceUserClientSetPipeIdle
	(IOService*)kMethodObjectThis,
	(IOMethod) &IOUSBInterfaceUserClient::SetPipeIdle,
	kIOUCScalarIScalarO,
	1,
	0
    },
    { //    kUSBInterfaceUserClientSetPipeActive
	(IOService*)kMethodObjectThis,
	(IOMethod) &IOUSBInterfaceUserClient::SetPipeActive,
	kIOUCScalarIScalarO,
	1,
	0
    },
    { //    kUSBInterfaceUserClientClearPipeStall
	(IOService*)kMethodObjectThis,
	(IOMethod) &IOUSBInterfaceUserClient::ClearPipeStall,
	kIOUCScalarIScalarO,
	2,
	0
    },
    { //    kUSBInterfaceUserClientControlRequestOut
	(IOService*)kMethodObjectThis,
	(IOMethod) &IOUSBInterfaceUserClient::ControlRequestOut,
	kIOUCScalarIStructI,
	4,
	0xffffffff
    },
    { //    kUSBInterfaceUserClientControlRequestIn
	(IOService*)kMethodObjectThis,
	(IOMethod) &IOUSBInterfaceUserClient::ControlRequestIn,
	kIOUCScalarIStructO,
	4,
	0xffffffff
    },
    { //    kUSBInterfaceUserClientControlRequestOutOOL
	(IOService*)kMethodObjectThis,
	(IOMethod) &IOUSBInterfaceUserClient::ControlRequestOutOOL,
	kIOUCStructIStructO,
	sizeof(IOUSBDevReqOOLTO),
	0
    },
    { //    kUSBInterfaceUserClientControlRequestInOOL
	(IOService*)kMethodObjectThis,
	(IOMethod) &IOUSBInterfaceUserClient::ControlRequestInOOL,
	kIOUCStructIStructO,
	sizeof(IOUSBDevReqOOLTO),
	sizeof(UInt32)
    },
    { //    kUSBInterfaceuserClientSetPipePolicy
	(IOService*)kMethodObjectThis,
	(IOMethod) &IOUSBInterfaceUserClient::SetPipePolicy,
	kIOUCScalarIScalarO,
	3,
	0
    },
    { //    kUSBInterfaceuserClientGetBandwidthAvailable
	(IOService*)kMethodObjectThis,
	(IOMethod) &IOUSBInterfaceUserClient::GetBandwidthAvailable,
	kIOUCScalarIScalarO,
	0,
	1
    },
    { //    kUSBInterfaceuserClientGetEndpointProperties
	(IOService*)kMethodObjectThis,
	(IOMethod) &IOUSBInterfaceUserClient::GetEndpointProperties,
	kIOUCScalarIScalarO,
	3,
	3
    },
    { //    kUSBInterfaceUserClientLowLatencyPrepareBuffer
	(IOService*)kMethodObjectThis,
	(IOMethod) &IOUSBInterfaceUserClient::LowLatencyPrepareBuffer,
	kIOUCStructIStructO,
	sizeof(LowLatencyUserBufferInfoV2),
	sizeof(UInt32)
    },
    { //    kUSBInterfaceUserClientLowLatencyReleaseBuffer
	(IOService*)kMethodObjectThis,
	(IOMethod) &IOUSBInterfaceUserClient::LowLatencyReleaseBuffer,
	kIOUCStructIStructO,
	sizeof(LowLatencyUserBufferInfoV2),
	0
    },
    { //    kUSBInterfaceUserClientGetMicroFrameNumber
        (IOService*)kMethodObjectThis,
        (IOMethod) &IOUSBInterfaceUserClient::GetMicroFrameNumber,
        kIOUCScalarIStructO,
        0,
        0xffffffff
    },
    { //    kUSBInterfaceUserClientGetFrameListTime
        (IOService*)kMethodObjectThis,
        (IOMethod) &IOUSBInterfaceUserClient::GetFrameListTime,
        kIOUCScalarIScalarO,
        0,
        1
    }
};



const IOExternalAsyncMethod 
IOUSBInterfaceUserClient::sAsyncMethods[kNumUSBInterfaceAsyncMethods] = {
    { //    kUSBDeviceUserClientSetAsyncPort
	(IOService*)kMethodObjectThis,
	(IOAsyncMethod) &IOUSBInterfaceUserClient::SetAsyncPort,
	kIOUCScalarIScalarO,
	0,
	0
    },
    { //    kUSBInterfaceUserClientControlAsyncRequestOut
	(IOService*)kMethodObjectThis,
	(IOAsyncMethod) &IOUSBInterfaceUserClient::ControlAsyncRequestOut,
	kIOUCStructIStructO,
	sizeof(IOUSBDevReqOOLTO),
	0
    },
    { //    kUSBInterfaceUserClientControlAsyncRequestIn
	(IOService*)kMethodObjectThis,
	(IOAsyncMethod) &IOUSBInterfaceUserClient::ControlAsyncRequestIn,
	kIOUCStructIStructO,
	sizeof(IOUSBDevReqOOLTO),
	0
    },
    { //    kUSBInterfaceUserClientAsyncReadPipe
	(IOService*)kMethodObjectThis,
	(IOAsyncMethod) &IOUSBInterfaceUserClient::AsyncReadPipe,
	kIOUCScalarIScalarO,
	5,
	0
    },
    { //    kUSBInterfaceUserClientAsyncWritePipe
	(IOService*)kMethodObjectThis,
	(IOAsyncMethod) &IOUSBInterfaceUserClient::AsyncWritePipe,
	kIOUCScalarIScalarO,
	5,
	0
    },
    { //    kUSBInterfaceUserClientReadIsochPipe
	(IOService*)kMethodObjectThis,
	(IOAsyncMethod) &IOUSBInterfaceUserClient::ReadIsochPipe,
	kIOUCStructIStructO,
	sizeof(IOUSBIsocStruct),
	0
    },
    { //    kUSBInterfaceUserClientWriteIsochPipe
	(IOService*)kMethodObjectThis,
	(IOAsyncMethod) &IOUSBInterfaceUserClient::WriteIsochPipe,
	kIOUCStructIStructO,
	sizeof(IOUSBIsocStruct),
	0
    },
    { //    kUSBInterfaceUserClientLowLatencyReadIsochPipe
	(IOService*)kMethodObjectThis,
	(IOAsyncMethod) &IOUSBInterfaceUserClient::LowLatencyReadIsochPipe,
	kIOUCStructIStructO,
	sizeof(IOUSBLowLatencyIsocStruct),
	0
    },
    { //    kUSBInterfaceUserClientLowLatencyWriteIsochPipe
	(IOService*)kMethodObjectThis,
	(IOAsyncMethod) &IOUSBInterfaceUserClient::LowLatencyWriteIsochPipe,
	kIOUCStructIStructO,
	sizeof(IOUSBLowLatencyIsocStruct),
	0
    }
};



void 
IOUSBInterfaceUserClient::SetExternalMethodVectors()
{
    fMethods = sMethods;
    fNumMethods = kNumUSBInterfaceMethods;
    fAsyncMethods = sAsyncMethods;
    fNumAsyncMethods = kNumUSBInterfaceAsyncMethods;
}



IOExternalMethod *
IOUSBInterfaceUserClient::getTargetAndMethodForIndex(IOService **target, UInt32 index)
{
    if (index < (UInt32)fNumMethods) 
    {
        if ((IOService*)kMethodObjectThis == fMethods[index].object)
            *target = this;
        else if ((IOService*)kMethodObjectOwner == fMethods[index].object)
            *target = fOwner;
        else
            return NULL;
	return (IOExternalMethod *) &fMethods[index];
    }
    else
	return NULL;
}



IOExternalAsyncMethod * 
IOUSBInterfaceUserClient::getAsyncTargetAndMethodForIndex(IOService **target, UInt32 index)
{
    if (index < (UInt32)fNumAsyncMethods) 
    {
        if ((IOService*)kMethodObjectThis == fAsyncMethods[index].object)
            *target = this;
        else if ((IOService*)kMethodObjectOwner == fAsyncMethods[index].object)
            *target = fOwner;
        else
            return NULL;
	return (IOExternalAsyncMethod *) &fAsyncMethods[index];
    }
    else
	return 0;
}


// Don't add any USBLogs to this routine.   You will panic if you use getName().
bool
IOUSBInterfaceUserClient::initWithTask(task_t owningTask,void *security_id , UInt32 type, OSDictionary * properties )
{
	if ( properties != NULL )
	{
		properties->setObject( kIOUserClientCrossEndianCompatibleKey, kOSBooleanTrue);
	}
	
    if (!super::initWithTask(owningTask, security_id , type, properties))
        return false;
	
    if (!owningTask)
		return false;
	
	// Allocate our expansion data
    //
    if (!fIOUSBInterfaceUserClientExpansionData)
    {
        fIOUSBInterfaceUserClientExpansionData = (IOUSBInterfaceUserClientExpansionData *)IOMalloc(sizeof(IOUSBInterfaceUserClientExpansionData));
        if (!fIOUSBInterfaceUserClientExpansionData)
            return false;
		
        bzero(fIOUSBInterfaceUserClientExpansionData, sizeof(IOUSBInterfaceUserClientExpansionData));
    }
	
    fTask = owningTask;
    fDead = false;
	
	// If bit 31 of type is set, then our client is running under Rosetta
	fClientRunningUnderRosetta = ( type & 0x80000000 );
    SetExternalMethodVectors();
	
    return true;
}



bool 
IOUSBInterfaceUserClient::start( IOService * provider )
{
    IOWorkLoop	*			workLoop = NULL;
    IOCommandGate *			commandGate = NULL;
	OSObject *				propertyObj = NULL;
    OSBoolean *				boolObj = NULL;

    USBLog(7, "+%s[%p]::start(%p)", getName(), this, provider);
    
    IncrementOutstandingIO();		// make sure we don't close until start is done

    fOwner = OSDynamicCast(IOUSBInterface, provider);

    if (!fOwner)
    {
        USBError(1, "%s[%p]::start - provider is NULL!", getName(), this);
        goto ErrorExit;
    }
    
    if(!super::start(provider))
    {
        USBError(1, "%s[%p]::start - super::start returned false!", getName(), this);
        goto ErrorExit;
    }

    commandGate = IOCommandGate::commandGate(this);

    if (!commandGate)
    {
        USBError(1, "%s[%p]::start - unable to create command gate", getName(), this);
        goto ErrorExit;
    }

    workLoop = getWorkLoop();
    if (!workLoop)
    {
        USBError(1, "%s[%p]::start - unable to find my workloop", getName(), this);
        goto ErrorExit;
    }
    workLoop->retain();
    
    if (workLoop->addEventSource(commandGate) != kIOReturnSuccess)
    {
        USBError(1, "%s[%p]::start - unable to add gate to work loop", getName(), this);
        goto ErrorExit;
    }

    fFreeUSBLowLatencyCommandPool = IOCommandPool::withWorkLoop(workLoop);
    if (!fFreeUSBLowLatencyCommandPool)
    {
        USBError(1,"%s[%p]::start - unable to create free command pool", getName(), this);
        
        // Remove the event source we added above
        //
        workLoop->removeEventSource(commandGate);
        
        goto ErrorExit;
    }

	// If our IOUSBDevice has a "Need contiguous memory for isoch" property, set a flag indicating so
	//
	propertyObj = fOwner->GetDevice()->copyProperty(kUSBControllerNeedsContiguousMemoryForIsoch);
    boolObj = OSDynamicCast( OSBoolean, propertyObj);
    if ( boolObj )
	{
		if ( boolObj->isTrue() )
			fNeedContiguousMemoryForLowLatencyIsoch = true;
		else
			fNeedContiguousMemoryForLowLatencyIsoch = false;
	}
	if (propertyObj)
		propertyObj->release();
		
    // Now that we have succesfully added our gate to the workloop, set our member variables
    //
    fGate = commandGate;
    fWorkLoop = workLoop;

    DecrementOutstandingIO();
    
    USBLog(7, "-%s[%p]::start", getName(), this);
    
    return true;
    
ErrorExit:
    
    if ( commandGate != NULL )
    {
        commandGate->release();
        commandGate = NULL;
    }
        
    if ( workLoop != NULL )
    {
        workLoop->release();
        workLoop = NULL;
    }
        
    DecrementOutstandingIO();
    return false;
}


// This method is NOT the IOService open(IOService*) method.
IOReturn 
IOUSBInterfaceUserClient::open(bool seize)
{
    IOOptionBits	options = seize ? (IOOptionBits)kIOServiceSeize : 0;

    USBLog(7, "+%s[%p]::open", getName(), this);
    
    if (!fOwner)
        return kIOReturnNotAttached;
        
    if (!fOwner->open(this, options))
    {
        USBLog(3, "+%s[%p]::open failed", getName(), this);
        return kIOReturnExclusiveAccess;
    }
    
	// If we are running under Rosetta, add a property to the interface nub:
	if ( fClientRunningUnderRosetta )
	{
		USBLog(5, "%s[%p]::open  setting kIOUserClientCrossEndianCompatibleKey TRUE on %p", getName(), this, fOwner);
		fOwner->setProperty(kIOUserClientCrossEndianCompatibleKey, kOSBooleanTrue);
	}
	else
	{
		USBLog(5, "%s[%p]::open  setting kIOUserClientCrossEndianCompatibleKey FALSE on %p", getName(), this, fOwner);
		fOwner->setProperty(kIOUserClientCrossEndianCompatibleKey, kOSBooleanFalse);
	}

    fNeedToClose = false;
    
    return kIOReturnSuccess;
}



// This is NOT the normal IOService::close(IOService*) method.
// We are treating this is a proxy that we should close our parent, but
// maintain the connection with the task
IOReturn
IOUSBInterfaceUserClient::close()
{
    IOReturn 	ret = kIOReturnSuccess;
    
    USBLog(7, "+%s[%p]::close", getName(), this);
    IncrementOutstandingIO();
    
    if (fOwner && !isInactive())
    {
		if (fOwner->isOpen(this))
		{
			fNeedToClose = true;				// the last outstanding IO will close this
			if (GetOutstandingIO() > 1)				// 1 for the one at the top of this routine
			{
				int		i;
				
				USBLog(6, "%s[%p]::close - outstanding IO, aborting pipes", getName(), this);
				for (i=1; i <= kUSBMaxPipes; i++)
					AbortPipe(i);
			}
		}
		else
			ret = kIOReturnNotOpen;
    }
    else
		ret = kIOReturnNotAttached;
	
    DecrementOutstandingIO();
    USBLog(7, "-%s[%p]::close - returning %x", getName(), this, ret);
    return ret;
}



//
// clientClose - my client on the user side has released the mach port, so I will no longer
// be talking to him
//
IOReturn  
IOUSBInterfaceUserClient::clientClose( void )
{
    USBLog(7, "+%s[%p]::clientClose(%p), IO: %ld", getName(), this, fUserClientBufferInfoListHead, fOutstandingIO);

    // Sleep for 1 ms to allow other threads that are pending to run
    //
    IOSleep(1);
    
    // We need to destroy the pipes so that any bandwidth gets returned -- our client has died so keeping them around 
    // is not useful
    //
    if ( fDead && fOwner && !isInactive() && fOwner->isOpen(this) )
	{
		// If the interface is other than 0, set it to 0 before closing the pipes
		UInt8	altSetting = fOwner->GetAlternateSetting();
		
		if ( altSetting != 0 )
		{
			USBLog(6, "+%s[%p]::clientClose setting Alternate Interface to 0 before closing pipes", getName(), this);
			fOwner->SetAlternateInterface(this, 0);
		}
		
        fOwner->ClosePipes();
	}
    
	// If we are already inactive, it means that our IOUSBInterface is going/has gone away.  In that case
	// we really do not need to do anything as the IOKit termination will take care of cleaning things.
	if ( !isInactive() )
	{
		if ( fOutstandingIO == 0 )
		{
			USBLog(6, "+%s[%p]::clientClose closing provider", getName(), this);

			if ( fOwner) 
			{
				// Since this is call that tells us that our user space client has gone away, we can
				// close our provider.  We don't set it to NULL because the IOKit object representing
				// it has not gone away.  That will come in thru did/willTerminate.  Also, we should
				// be checking whether fOwner was open before closing it, but we will do that later.
				fOwner->close(this);
			}
			
			if ( fDead) 
				release();
		}
		else
		{
			USBLog(5, "+%s[%p]::clientClose will close provider later", getName(), this);
			fNeedToClose = true;
		}
		
		fTask = NULL;
		
		terminate();
	}
	
    USBLog(7, "-%s[%p]::clientClose(%p)", getName(), this, fUserClientBufferInfoListHead);

    return kIOReturnSuccess;			// DONT call super::clientClose, which just returns notSupported
}


IOReturn 
IOUSBInterfaceUserClient::clientDied( void )
{
    IOReturn ret;

    USBLog(6, "+%s[%p]::clientDied() IO: %ld", getName(), this, fOutstandingIO);
    
    retain();                       // We will release once any outstandingIO is finished
        
    fDead = true;				// don't send any mach messages in this case
    ret = super::clientDied();

    USBLog(6, "-%s[%p]::clientDied()", getName(), this);

    return ret;
}



void
IOUSBInterfaceUserClient::ReqComplete(void *obj, void *param, IOReturn res, UInt32 remaining)
{
    void *	args[1];
    IOUSBInterfaceUserClientAsyncParamBlock * pb = (IOUSBInterfaceUserClientAsyncParamBlock *)param;
    IOUSBInterfaceUserClient *me = OSDynamicCast(IOUSBInterfaceUserClient, (OSObject*)obj);

    if (!me)
	return;
	
    USBLog(7, "%s[%p]::ReqComplete, req = %08x, remaining = %08x", me->getName(), me, (int)pb->fMax, (int)remaining);

    if(res == kIOReturnSuccess) 
    {
        args[0] = (void *)(pb->fMax - remaining);
    }
    else 
    {
        args[0] = 0;
    }
    if (pb->fMem)
    {
	pb->fMem->complete();
	pb->fMem->release();
        pb->fMem = NULL;
    }
    if (!me->fDead)
	sendAsyncResult(pb->fAsyncRef, res, args, 1);

    IOFree(pb, sizeof(*pb));
    me->DecrementOutstandingIO();
    me->release();
}


void
IOUSBInterfaceUserClient::IsoReqComplete(void *obj, void *param, IOReturn res, IOUSBIsocFrame *pFrames)
{
    void *	args[1];
    IOUSBInterfaceUserClientISOAsyncParamBlock * pb = (IOUSBInterfaceUserClientISOAsyncParamBlock *)param;
    IOUSBInterfaceUserClient *me = OSDynamicCast(IOUSBInterfaceUserClient, (OSObject*)obj);
	UInt32			i;
	
    if (!me)
		return;
	
	args[0] = pb->frameBase;
    pb->countMem->writeBytes(0, pb->frames, pb->frameLen);
    pb->dataMem->complete();
    pb->dataMem->release();
    pb->dataMem = NULL;
    
    pb->countMem->complete();
    pb->countMem->release();
    pb->countMem = NULL;
    
	if (!me->fDead)
		sendAsyncResult(pb->fAsyncRef, res, args, 1);
	
    IOFree(pb, sizeof(*pb)+pb->frameLen);
    me->DecrementOutstandingIO();
    me->release(); 
}




void
IOUSBInterfaceUserClient::LowLatencyIsoReqComplete(void *obj, void *param, IOReturn res, IOUSBLowLatencyIsocFrame *pFrames)
{
    void *			args[1];
    IOUSBLowLatencyCommand *	command = (IOUSBLowLatencyCommand *) param;
    IOMemoryDescriptor *	dataBufferDescriptor;
    OSAsyncReference		asyncRef;
    
    IOUSBInterfaceUserClient *	me = OSDynamicCast(IOUSBInterfaceUserClient, (OSObject*)obj);
	
    if (!me)
		return;
	
    args[0] = command->GetFrameBase(); 
    
    command->GetAsyncReference(&asyncRef);
    
    if (!me->fDead)
		sendAsyncResult( asyncRef, res, args, 1);
	
    // Complete the memory descriptor
    //
    dataBufferDescriptor = command->GetDataBuffer();
    dataBufferDescriptor->complete();
    dataBufferDescriptor->release();
    dataBufferDescriptor = NULL;
    
    // Free/give back the command 
    me->fFreeUSBLowLatencyCommandPool->returnCommand(command);
	
    me->DecrementOutstandingIO();
    me->release(); 
}


IOReturn 
IOUSBInterfaceUserClient::SetAlternateInterface(UInt8 altSetting)
{
    IOReturn	ret;
    
    USBLog(7, "+%s[%p]::SetAlternateInterface(%d)", getName(), this, altSetting);
    IncrementOutstandingIO();
    
    if (fOwner && !isInactive())
	ret = fOwner->SetAlternateInterface(this, altSetting);
    else
	ret = kIOReturnNotAttached;
	
    DecrementOutstandingIO();
    if (ret)
	USBLog(3, "%s[%p]::SetAlternateInterface - returning err %x", getName(), this, ret);
    return ret;
}



IOReturn 
IOUSBInterfaceUserClient::GetDevice(io_service_t *device)
{
    IOReturn		ret;
    
    IncrementOutstandingIO();

    if (fOwner && !isInactive())
    {
	// Although not documented, Simon says that exportObjectToClient consumes a reference,
	// so we have to put an extra retain on the device.  The user client side of the USB stack (USBLib)
	// always calls this routine in order to cache the USB device.  Radar #2586534
	fOwner->GetDevice()->retain();
	ret = exportObjectToClient(fTask, fOwner->GetDevice(), device);
    }
    else
        ret = kIOReturnNotAttached;

    DecrementOutstandingIO();
    if (ret)
	USBLog(3, "%s[%p]::GetDevice - returning err %x", getName(), this, ret);
    return ret;
}



IOReturn 
IOUSBInterfaceUserClient::GetFrameNumber(IOUSBGetFrameStruct *data, UInt32 *size)
{
    IOReturn		ret = kIOReturnSuccess;

    USBLog(7, "+%s[%p]::GetFrameNumber", getName(), this);

    if(*size != sizeof(IOUSBGetFrameStruct))
	return kIOReturnBadArgument;
    
    if (fOwner && !isInactive())
    {
	clock_get_uptime(&data->timeStamp);
	data->frame = fOwner->GetDevice()->GetBus()->GetFrameNumber();
    }
    else
        ret = kIOReturnNotAttached;

    if (ret)
	USBLog(3, "%s[%p]::GetFrameNumber - returning err %x", getName(), this, ret);
    return ret;
}



IOReturn
IOUSBInterfaceUserClient::GetMicroFrameNumber(IOUSBGetFrameStruct *data, UInt32 *size)
{
    // This method only available for v2 controllers
    //
    IOUSBControllerV2	*v2 = NULL;
    IOReturn		ret = kIOReturnSuccess;

	if (fOwner)
		v2 = OSDynamicCast(IOUSBControllerV2, fOwner->GetDevice()->GetBus());

    if (!v2)
    {
        USBLog(3, "%s[%p]::GetMicroFrameNumber - Not a USB 2.0 controller!  Returning 0x%x", getName(), this, kIOReturnNotAttached);
        return kIOReturnNotAttached;
    }

    if (*size != sizeof(IOUSBGetFrameStruct))
    {
        USBLog(3, "%s[%p]::GetMicroFrameNumber - *size is not sizeof(IOUSBGetFrameStruct): %ld, %ld", getName(), this, *size, sizeof(IOUSBGetFrameStruct) );
        return kIOReturnBadArgument;
    }

    IncrementOutstandingIO();
    if (fOwner && !isInactive())
    {
        clock_get_uptime(&data->timeStamp);
        data->frame = v2->GetMicroFrameNumber();
    }
    else
    {
        USBLog(3, "%s[%p]::GetMicroFrameNumber - no fOwner(%p) or isInactive", getName(), this, fOwner);
        ret = kIOReturnNotAttached;
    }

    DecrementOutstandingIO();
    if (ret)
        USBLog(3, "%s[%p]::GetFrameNumber - returning err %x", getName(), this, ret);
    return ret;
}



IOReturn
IOUSBInterfaceUserClient::GetBandwidthAvailable(UInt32 *bandwidth)
{
    IOReturn		ret = kIOReturnSuccess;

    USBLog(7, "+%s[%p]::GetBandwidthAvailable", getName(), this);

    if (fOwner && !isInactive())
    {
        *bandwidth = fOwner->GetDevice()->GetBus()->GetBandwidthAvailable();
    }
    else
        ret = kIOReturnNotAttached;

    if (ret)
        USBLog(3, "%s[%p]::GetBandwidthAvailable - returning err %x", getName(), this, ret);

    return ret;
}



IOReturn
IOUSBInterfaceUserClient::GetFrameListTime(UInt32 *microsecondsInFrame)
{
    IOReturn		ret = kIOReturnSuccess;

    USBLog(7, "+%s[%p]::GetFrameListTime", getName(), this);

    if (fOwner && !isInactive())
    {
        UInt8	speed;
        // Find the speed of the device and return the appropriate microseconds
        // depending on the speed
        //
        speed  = fOwner->GetDevice()->GetSpeed();

        if ( speed == kUSBDeviceSpeedHigh )
        {
            // High Speed Device
            //
            *microsecondsInFrame = kUSBHighSpeedMicrosecondsInFrame;
        }
        else
        {
            // Low and Full Speed
            //
            *microsecondsInFrame = kUSBFullSpeedMicrosecondsInFrame;
        }
    }
    else
        ret = kIOReturnNotAttached;

    if (ret)
        USBLog(3, "%s[%p]::GetFrameListTime - returning err %x", getName(), this, ret);

    return ret;
}


IOReturn 
IOUSBInterfaceUserClient::GetEndpointProperties(UInt8 alternateSetting, UInt8 endpointNumber, UInt8 direction, UInt32 *transferType, UInt32 *maxPacketSize, UInt32 *interval)
{
    IOReturn		ret = kIOReturnSuccess;
    UInt8		myTT;
    UInt16		myMPS;
    UInt8		myIV;

    USBLog(7, "+%s[%p]::GetEndpointProperties", getName(), this);

    if (fOwner && !isInactive())
	ret = fOwner->GetEndpointProperties(alternateSetting, endpointNumber, direction, &myTT, &myMPS, &myIV);
    else
        ret = kIOReturnNotAttached;

    if (ret)
	USBLog(4, "%s[%p]::GetEndpointProperties - returning err %x", getName(), this, ret);
    else
    {
        *transferType = myTT;
        *maxPacketSize = myMPS;
        *interval = myIV;
    }
    return ret;
}



IOUSBPipe*
IOUSBInterfaceUserClient::GetPipeObj(UInt8 pipeNo)
{
    IOUSBPipe *pipe = NULL;
    
    if (fOwner && !isInactive())
    {
        if (pipeNo == 0)
            pipe = fOwner->GetDevice()->GetPipeZero();
            
        if ((pipeNo > 0) && (pipeNo <= kUSBMaxPipes))
            pipe = fOwner->GetPipeObj(pipeNo-1);
    }
    // we need to retain the pipe object because it could actually get released by the device/interface
    // and we don't want it to go away. This retain should probably be done in IOUSBDevice or IOUSBInterface
    // but we would affect too many people if we did that right now.
    if (pipe)
        pipe->retain();
	
    return pipe;
}



// because of the way User Client works, these params need to be treated as pointers to 32 bit ints instead
// of pointers to smaller values, because on the other side they are automatically dereferenced as such
IOReturn
IOUSBInterfaceUserClient::GetPipeProperties(UInt8 pipeRef, UInt32 *direction, UInt32 *number, UInt32 *transferType, UInt32 *maxPacketSize, UInt32 *interval)
{
    IOUSBPipe 			*pipeObj;
    IOReturn			ret = kIOReturnSuccess;

    USBLog(7, "+%s[%p]::GetPipeProperties", getName(), this);

    IncrementOutstandingIO();
    if (fOwner && !isInactive())
    {
	pipeObj = GetPipeObj(pipeRef);    
	if(pipeObj)
	{
	    if (direction)
		*direction = pipeObj->GetDirection();
	    if (number)
		*number = pipeObj->GetEndpointNumber();
	    if (transferType)
		*transferType = pipeObj->GetType();
	    if (maxPacketSize)
		*maxPacketSize = pipeObj->GetMaxPacketSize();
	    if (interval)
		*interval = pipeObj->GetInterval();
	    pipeObj->release();
	}
	else
	    ret = kIOUSBUnknownPipeErr;
    }
    else
        ret = kIOReturnNotAttached;

    DecrementOutstandingIO();
    if (ret)
	USBLog(3, "%s[%p]::GetPipeProperties - returning err %x", getName(), this, ret);
    return ret;
}


IOReturn
IOUSBInterfaceUserClient::ReadPipe(UInt8 pipeRef, UInt32 noDataTimeout, UInt32 completionTimeout, void *buf, UInt32 *size)
{
    IOReturn 			ret;
    IOMemoryDescriptor *	mem;
    IOUSBPipe 			*pipeObj;

    USBLog(7, "+%s(%p)::ReadPipe(%d, %ld, %ld, %p, %p)", getName(), this, pipeRef, noDataTimeout, completionTimeout, buf, size);
    
    IncrementOutstandingIO();				// do this to "hold" ourselves until we complete
    
    if (fOwner && !isInactive())
    {
	pipeObj = GetPipeObj(pipeRef);
	if(pipeObj)
	{
	    mem = IOMemoryDescriptor::withAddress(buf, *size, kIODirectionIn);
	    if(mem)
	    { 
                *size = 0;
		ret = pipeObj->Read(mem, noDataTimeout, completionTimeout, 0, size);
		mem->release();
                mem = NULL;
	    }
	    else
		ret =  kIOReturnNoMemory;
	    pipeObj->release();
	}
	else
	    ret = kIOUSBUnknownPipeErr;
    }
    else
        ret = kIOReturnNotAttached;

    DecrementOutstandingIO();
    if (ret)
	USBLog(3, "%s[%p]::ReadPipe - returning err %x", getName(), this, ret);

    return ret;
}



/*
 * Out of line version of read pipe - the buffer isn't mapped (yet) into the kernel task
 */
IOReturn
IOUSBInterfaceUserClient::ReadPipeOOL(IOUSBBulkPipeReq *reqIn, UInt32 *sizeOut, IOByteCount inCount, IOByteCount *outCount)
{
    IOReturn 			ret;
    IOMemoryDescriptor *	mem;
    UInt8			pipeRef = reqIn->pipeRef;
    IOUSBPipe 			*pipeObj;

    USBLog(7, "+%s(%p)::ReadPipeOOL(%p, %p, %ld, %ld)", getName(), this, reqIn, sizeOut, inCount, *outCount);
    IncrementOutstandingIO();				// do this to "hold" ourselves until we complete
    
    if (fOwner && !isInactive())
    {
	pipeObj = GetPipeObj(pipeRef);
	if(pipeObj)
	{
            *sizeOut = 0;
	    mem = IOMemoryDescriptor::withAddress((vm_address_t)reqIn->buf, reqIn->size, kIODirectionIn, fTask);
	    if(mem)
	    {
		ret = mem->prepare();
		if(ret == kIOReturnSuccess)
		    ret = pipeObj->Read(mem, reqIn->noDataTimeout, reqIn->completionTimeout, 0, sizeOut);
		mem->complete();
		mem->release();
                mem = NULL;
	    }
	    else
	    {
		ret = kIOReturnNoMemory;
	    }
	    pipeObj->release();
	}
	else
	    ret = kIOUSBUnknownPipeErr;
    }
    else
        ret = kIOReturnNotAttached;

    DecrementOutstandingIO();
    if (ret)
	USBLog(3, "%s[%p]::ReadPipeOOL - returning err %x", getName(), this, ret);
    return ret;
}



IOReturn
IOUSBInterfaceUserClient::WritePipe(UInt8 pipeRef, UInt32 noDataTimeout, UInt32 completionTimeout, void *buf, UInt32 size)
{
    IOReturn 			ret;
    IOMemoryDescriptor *	mem;
    IOUSBPipe 			*pipeObj;

    USBLog(7, "+%s(%p)::WritePipe(%d, %ld, %ld, %p, 0x%lx)", getName(), this, pipeRef, noDataTimeout, completionTimeout, buf, size);
    IncrementOutstandingIO();				// do this to "hold" ourselves until we complete

    if (fOwner && !isInactive())
    {
	pipeObj = GetPipeObj(pipeRef);
	if(pipeObj)
	{
	    mem = IOMemoryDescriptor::withAddress(buf, size, kIODirectionOut);
	    if(mem) 
	    {
		ret = pipeObj->Write(mem, noDataTimeout, completionTimeout);
		mem->release();
                mem = NULL;
	    }
	    else
		ret = kIOReturnNoMemory;
	    pipeObj->release();
	}
	else
	    ret = kIOUSBUnknownPipeErr;
    }
    else
        ret = kIOReturnNotAttached;

    DecrementOutstandingIO();
    if (ret)
	USBLog(3, "%s[%p]::WritePipe - returning err %x", getName(), this, ret);
    return ret;
}



/*
 * Out of line version of write pipe - the buffer isn't mapped (yet) into the kernel task
 */
IOReturn
IOUSBInterfaceUserClient::WritePipeOOL(IOUSBBulkPipeReq *req, IOByteCount inCount)
{
    IOReturn 			ret;
    IOMemoryDescriptor *	mem;
    IOUSBPipe 			*pipeObj;

    USBLog(7, "+%s(%p)::WritePipeOOL(%ld)", getName(), this, inCount);
    
    IncrementOutstandingIO();				// do this to "hold" ourselves until we complete

    if (fOwner && !isInactive())
    {
	pipeObj = GetPipeObj(req->pipeRef);
	if(pipeObj)
	{
	    mem = IOMemoryDescriptor::withAddress((vm_address_t)req->buf, req->size, kIODirectionOut, fTask);
	    if(mem) 
	    {
		ret = mem->prepare();
		if(ret == kIOReturnSuccess)
		    ret = pipeObj->Write(mem, req->noDataTimeout, req->completionTimeout);
		mem->complete();
		mem->release();
                mem = NULL;
	    }
	    else
		ret = kIOReturnNoMemory;
	    pipeObj->release();
	}
	else
	    ret = kIOUSBUnknownPipeErr;
    }
    else
        ret = kIOReturnNotAttached;

    DecrementOutstandingIO();
    if (ret)
	USBLog(3, "%s[%p]::WritePipeOOL - returning err %x", getName(), this, ret);
    return ret;
}



IOReturn 
IOUSBInterfaceUserClient::GetPipeStatus(UInt8 pipeRef)
{
    IOUSBPipe 		*pipeObj;
    IOReturn		ret;
    
    USBLog(7, "+%s[%p]::GetPipeStatus", getName(), this);
    
    IncrementOutstandingIO();

    if (fOwner && !isInactive())
    {
	pipeObj = GetPipeObj(pipeRef);
	if(pipeObj)
	{
	    ret = pipeObj->GetPipeStatus();
	    pipeObj->release();
	}
	else
	    ret = kIOUSBUnknownPipeErr;
    }
    else
        ret = kIOReturnNotAttached;
    
    DecrementOutstandingIO();
    return ret;
}



IOReturn 
IOUSBInterfaceUserClient::AbortPipe(UInt8 pipeRef)
{
    IOUSBPipe 		*pipeObj;
    IOReturn		ret;
    
    USBLog(7, "+%s[%p]::AbortPipe", getName(), this);
    
    IncrementOutstandingIO();
    
    if (fOwner && !isInactive())
    {
	pipeObj = GetPipeObj(pipeRef);
	if(pipeObj)
	{
	    ret =  pipeObj->Abort();
	    pipeObj->release();
	}
	else
	    ret = kIOUSBUnknownPipeErr;
    }
    else
        ret = kIOReturnNotAttached;

    DecrementOutstandingIO();
    if (ret)
    {
        if ( ret == kIOUSBUnknownPipeErr )
            USBLog(6, "%s[%p]::AbortPipe - returning err %x", getName(), this, ret);
        else
            USBLog(3, "%s[%p]::AbortPipe - returning err %x", getName(), this, ret);
    }
    
    return ret;
}



IOReturn 
IOUSBInterfaceUserClient::ResetPipe(UInt8 pipeRef)
{
    IOUSBPipe 			*pipeObj;
    IOReturn			ret;

    USBLog(7, "+%s[%p]::ResetPipe", getName(), this);

    IncrementOutstandingIO();

    if (fOwner && !isInactive())
    {
	pipeObj = GetPipeObj(pipeRef);
	if(pipeObj)
	{
	    ret = pipeObj->Reset();
	    pipeObj->release();
	}
	else
	    ret = kIOUSBUnknownPipeErr;
    }
    else
        ret = kIOReturnNotAttached;

    DecrementOutstandingIO();
    if (ret)
	USBLog(3, "%s[%p]::ResetPipe - returning err %x", getName(), this, ret);
    
    return ret;
}



IOReturn 
IOUSBInterfaceUserClient::SetPipeIdle(UInt8 pipeRef)
{
    if (!fOwner || isInactive())
        return kIOReturnNotAttached;
        
    return(0);
}



IOReturn 
IOUSBInterfaceUserClient::SetPipeActive(UInt8 pipeRef)
{
    if (!fOwner || isInactive())
        return kIOReturnNotAttached;
        
    return(0);
}



IOReturn 
IOUSBInterfaceUserClient::ClearPipeStall(UInt8 pipeRef, bool bothEnds)
{
    IOUSBPipe 		*pipeObj;
    IOReturn		ret;

    USBLog(7, "+%s[%p]::ClearPipeStall", getName(), this);

    IncrementOutstandingIO();
    
    if (fOwner && !isInactive())
    {
	pipeObj = GetPipeObj(pipeRef);
	if(pipeObj)
	{
	    USBLog(6, "%s[%p]::ClearPipeStall = bothEnds = %d", getName(), this, bothEnds);
	    ret = pipeObj->ClearPipeStall(bothEnds);
	    pipeObj->release();
	}
	else
	    ret = kIOUSBUnknownPipeErr;
    }
    else
        ret = kIOReturnNotAttached;

    DecrementOutstandingIO();
    if (ret)
	USBLog(3, "%s[%p]::ClearPipeStall - returning err %x", getName(), this, ret);
    return ret;
}



IOReturn 
IOUSBInterfaceUserClient::SetPipePolicy(UInt8 pipeRef, UInt16 maxPacketSize, UInt8 maxInterval)
{
    IOUSBPipe 		*pipeObj;
    IOReturn		ret;

    USBLog(7, "+%s[%p]::SetPipePolicy", getName(), this);

    IncrementOutstandingIO();
    
    if (fOwner && !isInactive())
    {
	pipeObj = GetPipeObj(pipeRef);
	if(pipeObj)
	{
	    USBLog(5, "%s[%p]::SetPipePolicy(%d, %d)", getName(), this, maxPacketSize, maxInterval);
	    ret = pipeObj->SetPipePolicy(maxPacketSize, maxInterval);
	    pipeObj->release();
	}
	else
	    ret = kIOUSBUnknownPipeErr;
    }
    else
        ret = kIOReturnNotAttached;

    DecrementOutstandingIO();
    if (ret)
	USBLog(3, "%s[%p]::SetPipePolicy - returning err %x", getName(), this, ret);
    return ret;
}



/*
 * There's a limit of max 6 arguments to user client methods, so the type, recipient and request
 * are packed into one 16 bit integer.
 */
IOReturn
IOUSBInterfaceUserClient::ControlRequestOut(UInt32 param1, UInt32 param2, UInt32 noDataTimeout, UInt32 completionTimeout, void *buf, UInt32 size)
{
    IOReturn 		ret;
    IOUSBDevRequest	req;
    UInt8		pipeRef = (param1 >> 16) & 0xFF;
    UInt8		bmRequestType = (param1 >> 8) & 0xFF;
    UInt8		bRequest = param1 & 0xFF;
    UInt16		wValue = (param2 >> 16) & 0xFFFF;
    UInt16		wIndex = param2 & 0xFFFF;
    IOUSBPipe		*pipeObj;
    
    USBLog(7, "+%s[%p]::ControlRequestOut", getName(), this);

    IncrementOutstandingIO();
    if (fOwner && !isInactive())
    {
	pipeObj = GetPipeObj(pipeRef);
	if (pipeObj)
	{
	    req.bmRequestType = bmRequestType;
	    req.bRequest = bRequest;
	    req.wValue = wValue;
	    req.wIndex = wIndex;
	    req.wLength = size;
	    req.pData = buf;
	    ret = pipeObj->ControlRequest(&req, noDataTimeout, completionTimeout);
	
	    if(kIOReturnSuccess != ret) 
		USBLog(3, "%s[%p]::ControlRequestOut err:0x%x", getName(), this, ret);
	    pipeObj->release();
	}
	else
	    ret = kIOUSBUnknownPipeErr;
    }
    else
        ret = kIOReturnNotAttached;
	
    DecrementOutstandingIO();
    if (ret)
	USBLog(3, "%s[%p]::ControlRequestOut - returning err %x", getName(), this, ret);
    return ret;
}



IOReturn
IOUSBInterfaceUserClient::ControlRequestIn(UInt32 param1, UInt32 param2, UInt32 noDataTimeout, UInt32 completionTimeout, void *buf, UInt32 *size)
{
    IOReturn 		ret;
    IOUSBDevRequest	req;
    UInt8		pipeRef = (param1 >> 16) & 0xFF;
    UInt8		bmRequestType = (param1 >> 8) & 0xFF;
    UInt8		bRequest = param1 & 0xFF;
    UInt16		wValue = (param2 >> 16) & 0xFFFF;
    UInt16		wIndex = param2 & 0xFFFF;
    IOUSBPipe		*pipeObj;
    
    USBLog(7, "+%s[%p]::ControlRequestIn", getName(), this);

    IncrementOutstandingIO();

    if (fOwner && !isInactive())
    {
	pipeObj = GetPipeObj(pipeRef);
	if (pipeObj)
	{
	    req.bmRequestType = bmRequestType;
	    req.bRequest = bRequest;
	    req.wValue = wValue;
	    req.wIndex = wIndex;
	    req.wLength = *size;
	    req.pData = buf;
	    ret = pipeObj->ControlRequest(&req, noDataTimeout, completionTimeout);
	
	    if(ret == kIOReturnSuccess) 
		*size = req.wLenDone;
	    else 
	    {
		USBLog(3, "%s[%p]::ControlRequestIn err:0x%x", getName(), this, ret);
		*size = 0;
	    }
	    pipeObj->release();
	}
	else
	    ret = kIOUSBUnknownPipeErr;
    }
    else
        ret = kIOReturnNotAttached;

    DecrementOutstandingIO();
    if (ret)
	USBLog(3, "%s[%p]::ControlRequestIn - returning err %x", getName(), this, ret);
    return ret;
}



//
// ControlRequestOutOOL: reqIn->wLength > 4K
//
IOReturn
IOUSBInterfaceUserClient::ControlRequestOutOOL(IOUSBDevReqOOLTO *reqIn, IOByteCount inCount)
{
    IOReturn 			ret;
    IOUSBDevRequestDesc		req;
    IOMemoryDescriptor *	mem;
    IOUSBPipe *			pipeObj;

    USBLog(7, "+%s[%p]::ControlRequestOutOOL", getName(), this);

    if(inCount != sizeof(IOUSBDevReqOOLTO))
        return kIOReturnBadArgument;

    IncrementOutstandingIO();
    if (fOwner && !isInactive())
    {
	pipeObj = GetPipeObj(reqIn->pipeRef);
	if(pipeObj)
	{
	    mem = IOMemoryDescriptor::withAddress((vm_address_t)reqIn->pData, reqIn->wLength, kIODirectionOut, fTask);
	    if(mem) 
	    {
		req.bmRequestType = reqIn->bmRequestType;
		req.bRequest = reqIn->bRequest;
		req.wValue = reqIn->wValue;
		req.wIndex = reqIn->wIndex;
		req.wLength = reqIn->wLength;
		req.pData = mem;
	    
		ret = mem->prepare();
		if(ret == kIOReturnSuccess)
		    ret = pipeObj->ControlRequest(&req, reqIn->noDataTimeout, reqIn->completionTimeout);
		mem->complete();
		mem->release();
                mem = NULL;
	    }
	    else
		ret = kIOReturnNoMemory;
	    pipeObj->release();
	}
	else
	    ret = kIOUSBUnknownPipeErr;
    }
    else
        ret = kIOReturnNotAttached;

    DecrementOutstandingIO();
    if (ret)
	USBLog(3, "%s[%p]::ControlRequestOutOOL - returning err %x", getName(), this, ret);
    return ret;
}



//
// ControlRequestInOOL: reqIn->wLength > 4K
//
IOReturn
IOUSBInterfaceUserClient::ControlRequestInOOL(IOUSBDevReqOOLTO *reqIn, UInt32 *sizeOut, IOByteCount inCount, IOByteCount *outCount)
{
    IOReturn 			ret;
    IOUSBDevRequestDesc		req;
    IOMemoryDescriptor *	mem;
    IOUSBPipe *			pipeObj;

    USBLog(7, "+%s[%p]::ControlRequestInOOL", getName(), this);

    if((inCount != sizeof(IOUSBDevReqOOLTO)) || (*outCount != sizeof(UInt32)))
        return kIOReturnBadArgument;

    IncrementOutstandingIO();

    if (fOwner && !isInactive())
    {
	pipeObj = GetPipeObj(reqIn->pipeRef);
	if(pipeObj)
	{
	    mem = IOMemoryDescriptor::withAddress((vm_address_t)reqIn->pData, reqIn->wLength, kIODirectionIn, fTask);
	    if(mem) 
	    {
		req.bmRequestType = reqIn->bmRequestType;
		req.bRequest = reqIn->bRequest;
		req.wValue = reqIn->wValue;
		req.wIndex = reqIn->wIndex;
		req.wLength = reqIn->wLength;
		req.pData = mem;
	    
		ret = mem->prepare();
		if(ret == kIOReturnSuccess)
		    ret = pipeObj->ControlRequest(&req, reqIn->noDataTimeout, reqIn->completionTimeout);
	
		mem->complete();
		mem->release();
                mem = NULL;
		if(ret == kIOReturnSuccess) 
		{
		    *sizeOut = req.wLenDone;
		}
		else 
		{
		    USBLog(3, "%s[%p]::ControlRequestInOOL err:0x%x", getName(), this, ret);
		    *sizeOut = 0;
		}
		*outCount = sizeof(UInt32);
	    }
	    else
		ret = kIOReturnNoMemory;
	    pipeObj->release();
	}
	else
	    ret = kIOUSBUnknownPipeErr;
    }
    else
        ret = kIOReturnNotAttached;

    DecrementOutstandingIO();
    if (ret)
	USBLog(3, "%s[%p]::ControlRequestInOOL - returning err %x", getName(), this, ret);
    return ret;
}


IOReturn 
IOUSBInterfaceUserClient::LowLatencyPrepareBuffer(LowLatencyUserBufferInfoV2 *bufferData, UInt32 * addrOut, IOByteCount inCount, IOByteCount *outCount)
{
    IOReturn								ret = kIOReturnSuccess;
    IOMemoryDescriptor *					aDescriptor = NULL;
    IOUSBLowLatencyUserClientBufferInfo *	kernelDataBuffer = NULL;
    IOMemoryMap *							frameListMap = NULL;
    IODirection								direction;
	IOBufferMemoryDescriptor *				uhciDescriptor = NULL;
	IOMemoryMap *							uhciMap = NULL;
	void *									uhciMappedData = NULL;
	bool									preparedUHCIDescriptor = false;
    
    IncrementOutstandingIO();
    
    if (fOwner && !isInactive())
    {
         USBLog(3, "%s[%p]::LowLatencyPrepareBuffer cookie: %ld, buffer: %p, size: %ld, type %ld, isPrepared: %d, next: %p", getName(), this,
           bufferData->cookie,
            bufferData->bufferAddress,
            bufferData->bufferSize,
            bufferData->bufferType,
            bufferData->isPrepared,
            bufferData->nextBuffer);
            
		*addrOut = 0;

		// Allocate a buffer and zero it
        //
        kernelDataBuffer = ( IOUSBLowLatencyUserClientBufferInfo *) IOMalloc( sizeof(IOUSBLowLatencyUserClientBufferInfo) );
        if (kernelDataBuffer == NULL )
        {
            USBLog(1,"%s[%p]::LowLatencyPrepareBuffer  Could not malloc buffer info (size = %ld)!", getName(), this, sizeof(IOUSBLowLatencyUserClientBufferInfo) );
            return kIOReturnNoMemory;
        }
        
        bzero(kernelDataBuffer, sizeof(IOUSBLowLatencyUserClientBufferInfo));
        
        // Set the known fields
        //
        kernelDataBuffer->cookie = bufferData->cookie;
        kernelDataBuffer->bufferType = bufferData->bufferType;
        
        // If we are on a UHCI controller and this is a low latency buffer, we need to allocate the data here and share it with user space, as UHCI requires
		// contiguous memory   
        //
        if ( fNeedContiguousMemoryForLowLatencyIsoch and ((bufferData->bufferType == kUSBLowLatencyWriteBuffer) or (bufferData->bufferType == kUSBLowLatencyReadBuffer)) )
		{
			USBLog(1,"%s[%p]::LowLatencyPrepareBuffer  About to call to get a contiguous bufferMemoryDescriptor( %ld)!", getName(), this, bufferData->bufferSize );
            direction = ( bufferData->bufferType == kUSBLowLatencyWriteBuffer ? kIODirectionOut : kIODirectionIn );
			uhciDescriptor = IOBufferMemoryDescriptor::withOptions( direction | kIOMemoryPhysicallyContiguous | kIOMemoryKernelUserShared, bufferData->bufferSize, PAGE_SIZE);
			USBLog(1,"%s[%p]::LowLatencyPrepareBuffer  Returned %p", getName(), this, uhciDescriptor );
			
			if ( uhciDescriptor == NULL)
			{
				USBLog(1,"%s[%p]::LowLatencyPrepareBuffer  Could not create a physically contiguous IOBMD (size %ld)!", getName(), this, bufferData->bufferSize );
				ret = kIOReturnNoMemory;
				goto ErrorExit;
			}
			
			ret = uhciDescriptor->prepare();
            if (ret != kIOReturnSuccess)
            {
                USBLog(1,"%s[%p]::LowLatencyPrepareBuffer  Could not prepare the data buffer memory descriptor 0x%x!", getName(), this, ret );
                goto ErrorExit;
            }
					
			preparedUHCIDescriptor = true;
			
			uhciMap = uhciDescriptor->map(fTask, NULL, kIOMapAnywhere, 0, 0	);
			if ( uhciMap == NULL )
			{
                USBLog(1,"%s[%p]::LowLatencyPrepareBuffer  Could not map the data buffer memory descriptor", getName(), this );
				ret = kIOReturnVMError;
                goto ErrorExit;
			}
			
			uhciMappedData = (void *) uhciMap->getVirtualAddress();
			if ( uhciMappedData == NULL )
			{
                USBLog(1,"%s[%p]::LowLatencyPrepareBuffer  Could not get the virtual address of the map", getName(), this );
				ret = kIOReturnVMError;
                goto ErrorExit;
			}
			
			USBLog(1,"%s[%p]::LowLatencyPrepareBuffer  mapped virtual address = %p", getName(), this, uhciMappedData );
			
			UInt32 * theAddr = (UInt32 *) uhciMappedData;
			
			// At this point, we have the contiguous buffer used for the UHCI Low Latency Writes, so save it in our data structure so we can clean up later on
			//
            kernelDataBuffer->bufferSize = bufferData->bufferSize;
			kernelDataBuffer->writeDescritporForUHCI = uhciDescriptor;
			kernelDataBuffer->writeMapForUHCI= uhciMap;

			*addrOut = (UInt32)uhciMappedData;
		}
        else if ( (bufferData->bufferType == kUSBLowLatencyWriteBuffer) or ( bufferData->bufferType == kUSBLowLatencyReadBuffer) )
        {
            // We have a data buffer, so create a IOMD and prepare it
            //
            direction = ( bufferData->bufferType == kUSBLowLatencyWriteBuffer ? kIODirectionOut : kIODirectionIn );
            aDescriptor = IOMemoryDescriptor::withAddress((vm_address_t)bufferData->bufferAddress, bufferData->bufferSize, direction, fTask);
            if(!aDescriptor) 
            {
                USBLog(1,"%s[%p]::LowLatencyPrepareBuffer  Could not create a data buffer memory descriptor (addr: %p, size %ld)!", getName(), this, bufferData->bufferAddress, bufferData->bufferSize );
                ret = kIOReturnNoMemory;
                goto ErrorExit;
            }

            ret = aDescriptor->prepare();
            if (ret != kIOReturnSuccess)
            {
                USBLog(1,"%s[%p]::LowLatencyPrepareBuffer  Could not prepare the data buffer memory descriptor!", getName(), this );
                goto ErrorExit;
            }

            
            // OK, now save this in our user client structure
            // 
            kernelDataBuffer->bufferAddress = bufferData->bufferAddress;
            kernelDataBuffer->bufferSize = bufferData->bufferSize;
            kernelDataBuffer->bufferDescriptor = aDescriptor;
            
            USBLog(3, "%s[%p]::LowLatencyPrepareBuffer  finished preparing data buffer: %p, size %ld, desc: %p, cookie: %ld", getName(), this,
                    kernelDataBuffer->bufferAddress, kernelDataBuffer->bufferSize, kernelDataBuffer->bufferDescriptor, kernelDataBuffer->cookie);
        }
        else if ( bufferData->bufferType == kUSBLowLatencyFrameListBuffer )
        {
            // We have a frame list that we need to map to the kernel's memory space
            //
            // Create a memory descriptor for our frame list and prepare it (pages it in if necesary and prepares it). 
            //
            aDescriptor = IOMemoryDescriptor::withAddress((vm_address_t)bufferData->bufferAddress, bufferData->bufferSize, kIODirectionOutIn, fTask);
            if(!aDescriptor) 
            {
                USBLog(1,"%s[%p]::LowLatencyPrepareBuffer  Could not create a frame list memory descriptor (addr: %p, size %ld)!", getName(), this, bufferData->bufferAddress, bufferData->bufferSize );
                ret = kIOReturnNoMemory;
                goto ErrorExit;
            }
            
            ret = aDescriptor->prepare();
            if (ret != kIOReturnSuccess)
            {
                USBLog(1,"%s[%p]::LowLatencyPrepareBuffer  Could not prepare the frame list memory descriptor!", getName(), this );
                ret = kIOReturnNoMemory;
                goto ErrorExit;
            }
            

            // Map it into the kernel
            //
            frameListMap = aDescriptor->map();
            if (!frameListMap) 
            {
                USBLog(1,"%s[%p]::LowLatencyPrepareBuffer  Could not map the frame list memory descriptor!", getName(), this );
                ret = kIOReturnNoMemory;
                aDescriptor->complete();
                goto ErrorExit;
            }

            // Get the the mapped in virtual address and save it
            //
            kernelDataBuffer->frameListKernelAddress = frameListMap->getVirtualAddress();
            
            // Save the rest of the items
            // 
            kernelDataBuffer->bufferAddress = bufferData->bufferAddress;
            kernelDataBuffer->bufferSize = bufferData->bufferSize;
            kernelDataBuffer->frameListDescriptor = aDescriptor;
            kernelDataBuffer->frameListMap = frameListMap;

            USBLog(3, "%s[%p]::LowLatencyPrepareBuffer  finished preparing frame list buffer: %p, size %ld, desc: %p, map %p, kernel address: 0x%x, cookie: %ld", getName(), this,
                    kernelDataBuffer->bufferAddress, kernelDataBuffer->bufferSize, kernelDataBuffer->bufferDescriptor, kernelDataBuffer->frameListMap,
                    kernelDataBuffer->frameListKernelAddress,  kernelDataBuffer->cookie);
        }

        // Cool, we have a good buffer, add it to our list
        //
        AddDataBufferToList( kernelDataBuffer );
        
    }
    else
        ret = kIOReturnNotAttached;

ErrorExit:

    if (ret)
	{
        USBLog(3, "%s[%p]::LowLatencyPrepareBuffer - returning err %x", getName(), this, ret);
		
		if ( uhciDescriptor )
		{
			if (preparedUHCIDescriptor)
				uhciDescriptor->complete();
			
			uhciDescriptor->release();
		}
		
		if ( uhciMap != NULL )
			uhciMap->release();
		
	}
	
    DecrementOutstandingIO();
    
    return ret;
}

IOReturn 
IOUSBInterfaceUserClient::LowLatencyReleaseBuffer(LowLatencyUserBufferInfoV2 *dataBuffer)
{
    IOUSBLowLatencyUserClientBufferInfo *	kernelDataBuffer	= NULL;
    IOReturn				ret 			= kIOReturnSuccess;
    bool				found 			= false;
    
    IncrementOutstandingIO();
    
    USBLog(3, "+%s[%p]::LowLatencyReleaseBuffer for cookie: %ld", getName(), this, dataBuffer->cookie);

    if (fOwner && !isInactive())
    {
        // We need to find the LowLatencyUserBufferInfoV2 structure that contains
        // this buffer and then remove it from the list and free the structure
        // and the memory that was allocated for it
        //
        kernelDataBuffer = FindBufferCookieInList( dataBuffer->cookie );
        if ( kernelDataBuffer == NULL )
        {
            USBLog(3, "+%s[%p]::LowLatencyReleaseBuffer cookie: %ld, could not find buffer in list", getName(), this, dataBuffer->cookie);
            ret = kIOReturnBadArgument;
            goto ErrorExit;
        }
        
        // Now, remove this bufferData from the list
        //
        found = RemoveDataBufferFromList( kernelDataBuffer );
        if ( !found )
        {
            USBLog(3, "+%s[%p]::LowLatencyReleaseBuffer cookie: %ld, could not remove buffer (%p) from list", getName(), this, dataBuffer->cookie, kernelDataBuffer);
            ret = kIOReturnBadArgument;
            goto ErrorExit;
        }

        // Now, need to complete/release/free the objects we allocated in our prepare
        //
        if ( kernelDataBuffer->frameListMap )
        {
            USBLog(5, "+IOUSBInterfaceUserClient[%p]::LowLatencyReleaseBuffer releasing frameListMap (0x%x)", this, kernelDataBuffer->frameListKernelAddress);
            kernelDataBuffer->frameListMap->release();
            kernelDataBuffer->frameListMap = NULL;
            kernelDataBuffer->frameListKernelAddress = NULL;
        }
            
        if ( kernelDataBuffer->frameListDescriptor )
        {
            kernelDataBuffer->frameListDescriptor->complete();
            kernelDataBuffer->frameListDescriptor->release();
            kernelDataBuffer->frameListDescriptor = NULL;
        }
        
        if ( kernelDataBuffer->bufferDescriptor )
        {
            kernelDataBuffer->bufferDescriptor->complete();
            kernelDataBuffer->bufferDescriptor->release();
            kernelDataBuffer->bufferDescriptor = NULL;
        }
		
        if ( kernelDataBuffer->writeDescritporForUHCI )
        {
            kernelDataBuffer->writeDescritporForUHCI->complete();
            kernelDataBuffer->writeDescritporForUHCI->release();
            kernelDataBuffer->writeDescritporForUHCI = NULL;
        }
		
        if ( kernelDataBuffer->writeMapForUHCI )
        {
            USBLog(3, "+IOUSBInterfaceUserClient[%p]::LowLatencyReleaseBuffer releasing uhciMap (%p)", this, kernelDataBuffer->writeMapForUHCI);
            kernelDataBuffer->writeMapForUHCI->release();
            kernelDataBuffer->writeMapForUHCI = NULL;
        }
        
        // Finally, deallocate our kernelDataBuffer
        //
        IOFree(kernelDataBuffer, sizeof(IOUSBLowLatencyUserClientBufferInfo));

    }
    else
        ret = kIOReturnNotAttached;

ErrorExit:

    if (ret)
        USBLog(3, "%s[%p]::LowLatencyReleaseBuffer - returning err %x", getName(), this, ret);

    DecrementOutstandingIO();
    return ret;
}

void
IOUSBInterfaceUserClient::AddDataBufferToList( IOUSBLowLatencyUserClientBufferInfo * insertBuffer )
{
    IOUSBLowLatencyUserClientBufferInfo *	buffer;
    
    // Traverse the list looking for last buffer and insert ours into it
    //
    if ( fUserClientBufferInfoListHead == NULL )
    {
        fUserClientBufferInfoListHead = insertBuffer;
        return;
    }
    
    buffer = fUserClientBufferInfoListHead;
    
    while ( buffer->nextBuffer != NULL )
    {
        buffer = buffer->nextBuffer;
    }
    
    // When we get here, nextBuffer is pointing to NULL.  Our insert buffer
    // already has nextBuffer = NULL, so we just insert it
    //
    buffer->nextBuffer = insertBuffer;
}

IOUSBLowLatencyUserClientBufferInfo *	
IOUSBInterfaceUserClient::FindBufferCookieInList( UInt32 cookie)
{
    IOUSBLowLatencyUserClientBufferInfo *	buffer;
    bool				foundIt = true;
    
    // Traverse the list looking for this buffer
    //
    if ( fUserClientBufferInfoListHead == NULL )
    {
        USBLog(3, "%s[%p]::FindBufferCookieInList - fUserClientBufferInfoListHead was NULL", getName(), this);
        return NULL;
    }
    
    buffer = fUserClientBufferInfoListHead;
    
    // Now, we need to see if our cookie is the same as one in the buffer list
    //
    while ( buffer->cookie != cookie )
    {
        buffer = buffer->nextBuffer;
        if ( buffer == NULL )
        {
            foundIt = false;
            break;
        }
    }
    
    if ( foundIt )
        return buffer;
    else
	{
        USBLog(3, "%s[%p]::FindBufferCookieInList - Could no find buffer for cookie (%ld), returning NULL", getName(), this, cookie);
        return NULL;
	}
}

 bool			
IOUSBInterfaceUserClient::RemoveDataBufferFromList( IOUSBLowLatencyUserClientBufferInfo *removeBuffer)
{
    IOUSBLowLatencyUserClientBufferInfo *	buffer;
    IOUSBLowLatencyUserClientBufferInfo *	previousBuffer;
    
    // If our head is NULL, then this buffer does not exist in our list
    //
    if ( fUserClientBufferInfoListHead == NULL )
    {
        return false;
    }
    
    buffer = fUserClientBufferInfoListHead;
    
    // if our removeBuffer is the first one in the list, then just update the head and
    // exit
    //
    if ( buffer == removeBuffer )
    {
        fUserClientBufferInfoListHead = buffer->nextBuffer;
    }
    else
    {    
        // Need to start previousBuffer pointing to our initial buffer, in case we match
        // the first time
        //
        previousBuffer = buffer;
        
        while ( buffer->nextBuffer != removeBuffer )
        {
            previousBuffer = buffer;
            buffer = previousBuffer->nextBuffer;
        }
        
        // When we get here, buffer is pointing to the same buffer as removeBuffer
        // and previous buffer is pointing to the previous element in the link list,
        // so, update the link in previous to point to removeBuffer->nextBuffer;
        //
        buffer->nextBuffer = removeBuffer->nextBuffer;
    }
    
    return true;
}


// ASYNC METHODS

IOReturn 
IOUSBInterfaceUserClient::SetAsyncPort(OSAsyncReference asyncRef)
{
    if (!fOwner || isInactive())
        return kIOReturnNotAttached;
        
    fWakePort = (mach_port_t) asyncRef[0];
    return kIOReturnSuccess;
}


IOReturn
IOUSBInterfaceUserClient::ControlAsyncRequestOut(OSAsyncReference asyncRef, IOUSBDevReqOOLTO *reqIn, IOByteCount inCount)
{
    IOReturn 			ret = kIOReturnSuccess;
    IOUSBPipe *			pipeObj;

    IOUSBCompletion		tap;
    IOUSBInterfaceUserClientAsyncParamBlock * 			pb = NULL;
    IOMemoryDescriptor *	mem = NULL;

    USBLog(7, "+%s[%p]::ControlAsyncRequestOut", getName(), this);

    if(inCount != sizeof(IOUSBDevReqOOLTO))
        return kIOReturnBadArgument;

    retain();					// to make sure ReqComplete is still around
    IncrementOutstandingIO();			// to make sure ReqComplete is still around

    if (fOwner && !isInactive())
    {
	pipeObj = GetPipeObj(reqIn->pipeRef);
	if(pipeObj)
	{
	    if (reqIn->wLength)
	    {
		if (reqIn->pData)
		{
		    mem = IOMemoryDescriptor::withAddress((vm_address_t)reqIn->pData, reqIn->wLength, kIODirectionOut, fTask);
		    if (!mem)
			ret = kIOReturnNoMemory;
		}
		else
		    ret = kIOReturnBadArgument;
	    }
	    if (ret == kIOReturnSuccess)
	    {
		pb = (IOUSBInterfaceUserClientAsyncParamBlock *)IOMalloc(sizeof(IOUSBInterfaceUserClientAsyncParamBlock));
		if(!pb) 
		    ret = kIOReturnNoMemory;
	    }
	    
	    if (mem && (ret == kIOReturnSuccess))
		ret = mem->prepare();
	
	    if (ret == kIOReturnSuccess)
	    {
		pb->req.bmRequestType = reqIn->bmRequestType;
		pb->req.bRequest = reqIn->bRequest;
		pb->req.wValue = reqIn->wValue;
		pb->req.wIndex = reqIn->wIndex;
		pb->req.wLength = reqIn->wLength;
		pb->req.pData = mem;
	    
		bcopy(asyncRef, pb->fAsyncRef, sizeof(OSAsyncReference));
		pb->fMax = reqIn->wLength;
		pb->fMem = mem;
		tap.target = this;
		tap.action = &ReqComplete;	// Want same number of reply args as for ControlReqIn
		tap.parameter = pb;
		ret = pipeObj->ControlRequest(&pb->req, reqIn->noDataTimeout, reqIn->completionTimeout, &tap);
	    }
	    pipeObj->release();
	}
	else
	    ret = kIOUSBUnknownPipeErr;
    }
    else
        ret = kIOReturnNotAttached;
    
    if(ret != kIOReturnSuccess) 
    {
	USBLog(3, "%s[%p]::ControlAsyncRequestOut err 0x%x", getName(), this, ret);
	if(mem) 
	{
	    mem->complete();
	    mem->release();
            mem = NULL;
	}
	if(pb)
	    IOFree(pb, sizeof(*pb));
	
        // only decrement if we are not going to be successful. otherwise, this will be done in the completion
	DecrementOutstandingIO();
        release();
    }
    return ret;
}



IOReturn
IOUSBInterfaceUserClient::ControlAsyncRequestIn(OSAsyncReference asyncRef, IOUSBDevReqOOLTO *reqIn, IOByteCount inCount)
{
    IOReturn 			ret = kIOReturnSuccess;
    IOUSBPipe *			pipeObj;

    IOUSBCompletion		tap;
    IOUSBInterfaceUserClientAsyncParamBlock * 			pb = NULL;
    IOMemoryDescriptor *	mem = NULL;

    USBLog(7, "+%s[%p]::ControlAsyncRequestIn", getName(), this);

    if(inCount != sizeof(IOUSBDevReqOOLTO))
        return kIOReturnBadArgument;

    retain();
    IncrementOutstandingIO();		// to make sure ReqComplete is still around

    if (fOwner && !isInactive())
    {
	pipeObj = GetPipeObj(reqIn->pipeRef);
	if(pipeObj)
	{
	    if (reqIn->wLength)
	    {
		if (reqIn->pData)
		{
		    mem = IOMemoryDescriptor::withAddress((vm_address_t)reqIn->pData, reqIn->wLength, kIODirectionIn, fTask);
		    if (!mem)
			ret = kIOReturnNoMemory;
		}
		else
		    ret = kIOReturnBadArgument;
	    }
	    if (ret == kIOReturnSuccess)
	    {
		pb = (IOUSBInterfaceUserClientAsyncParamBlock *)IOMalloc(sizeof(IOUSBInterfaceUserClientAsyncParamBlock));
		if(!pb) 
		    ret = kIOReturnNoMemory;
	    }
	    if (mem && (ret == kIOReturnSuccess))
		ret = mem->prepare();
	
	    if (ret == kIOReturnSuccess)
	    {
		pb->req.bmRequestType = reqIn->bmRequestType;
		pb->req.bRequest = reqIn->bRequest;
		pb->req.wValue = reqIn->wValue;
		pb->req.wIndex = reqIn->wIndex;
		pb->req.wLength = reqIn->wLength;
		pb->req.pData = mem;
	    
		bcopy(asyncRef, pb->fAsyncRef, sizeof(OSAsyncReference));
		pb->fMax = reqIn->wLength;
		pb->fMem = mem;
		tap.target = this;
		tap.action = &ReqComplete;
		tap.parameter = pb;
		ret = pipeObj->ControlRequest(&pb->req, reqIn->noDataTimeout, reqIn->completionTimeout, &tap);
	    }
	    pipeObj->release();
	}
	else
	    ret = kIOUSBUnknownPipeErr;
    }
    else
        ret = kIOReturnNotAttached;
        
    if(ret != kIOReturnSuccess) 
    {
	USBLog(3, "%s[%p]::ControlAsyncRequestIn err 0x%x", getName(), this, ret);
	if(mem) 
	{
	    mem->complete();
	    mem->release();
            mem = NULL;
	}
	if(pb)
	    IOFree(pb, sizeof(*pb));
	
        // only decrement if we are not going to be successful. otherwise, this will be done in the completion
	DecrementOutstandingIO();
        release();
    }

    return ret;
}



IOReturn 
IOUSBInterfaceUserClient::DoIsochPipeAsync(OSAsyncReference asyncRef, IOUSBIsocStruct *stuff, IODirection direction)
{
    IOReturn				ret;
    IOUSBPipe *				pipeObj;
    IOUSBIsocCompletion		tap;
    IOMemoryDescriptor *	dataMem = NULL;
    IOMemoryDescriptor *	countMem = NULL;
    int						frameLen = 0;	// In bytes
    bool					countMemPrepared = false;
    bool					dataMemPrepared = false;
    IOUSBInterfaceUserClientISOAsyncParamBlock * 		pb = NULL;
	
    USBLog(7, "+%s[%p]::DoIsochPipeAsync", getName(), this);
    retain();
	USBLog(8,"+%s[%p]::DoIsochPipeAsync  fPipe: %ld, fBuffer: %p, fBufSize = 0x%lx, fStartFrame: 0x%qx, fNumFrames: 0x%lx, fFramecounts: %p, crossEndian: %d", getName(), this,
		   stuff->fPipe, stuff->fBuffer, stuff->fBufSize, stuff->fStartFrame, stuff->fNumFrames, stuff->fFrameCounts, fClientRunningUnderRosetta);
	
    IncrementOutstandingIO();		// to make sure IsoReqComplete is still around
    
    if (fOwner && !isInactive())
    {
		pipeObj = GetPipeObj(stuff->fPipe);
		if(pipeObj)
		{
			frameLen = stuff->fNumFrames * sizeof(IOUSBIsocFrame);
			do {
				dataMem = IOMemoryDescriptor::withAddress((vm_address_t)stuff->fBuffer, stuff->fBufSize, direction, fTask);
				if(!dataMem) 
				{
                    USBLog(1, "%s[%p]::DoIsochPipeAsync could not create dataMem descriptor", getName(), this);
					ret = kIOReturnNoMemory;
					break;
				}
				ret = dataMem->prepare();
				if (ret != kIOReturnSuccess)
                {
                    USBLog(1, "%s[%p]::DoIsochPipeAsync could not prepare dataMem descriptor (0x%x)", getName(), this, ret);
					break;
                }
				
                dataMemPrepared = true;
                
				countMem = IOMemoryDescriptor::withAddress((vm_address_t)stuff->fFrameCounts, frameLen, kIODirectionOutIn, fTask);
				if(!countMem) 
				{
                    USBLog(1, "%s[%p]::DoIsochPipeAsync could not create countMem descriptor", getName(), this);
					ret = kIOReturnNoMemory;
					break;
				}
				
                ret = countMem->prepare();
                if (ret != kIOReturnSuccess)
                {
                    USBLog(1, "%s[%p]::DoIsochPipeAsync could not prepare dataMem descriptor (0x%x)", getName(), this, ret);
                    break;
                }
                countMemPrepared = true;
				
                // Copy in requested transfers, we'll copy out result in completion routine
				pb = (IOUSBInterfaceUserClientISOAsyncParamBlock *)IOMalloc(sizeof(IOUSBInterfaceUserClientISOAsyncParamBlock) + frameLen);
				if(!pb) 
				{
					ret = kIOReturnNoMemory;
					break;
				}
                
                bcopy(asyncRef, pb->fAsyncRef, sizeof(OSAsyncReference));
				pb->frameLen = frameLen;
				pb->frameBase = stuff->fFrameCounts;
				pb->numFrames = stuff->fNumFrames;
				pb->dataMem = dataMem;
				pb->countMem = countMem;
				countMem->readBytes(0, pb->frames, frameLen);
				tap.target = this;
				tap.action = &IsoReqComplete;
				tap.parameter = pb;
				if(direction == kIODirectionOut)
					ret = pipeObj->Write(dataMem, stuff->fStartFrame, stuff->fNumFrames, pb->frames, &tap);
				else
					ret = pipeObj->Read(dataMem, stuff->fStartFrame, stuff->fNumFrames, pb->frames, &tap);
			} while (false);
			pipeObj->release();
		}
		else
			ret = kIOUSBUnknownPipeErr;
    }
    else
        ret = kIOReturnNotAttached;
	
    if(kIOReturnSuccess != ret) 
    {
		USBLog(3, "%s[%p]::DoIsochPipeAsync err 0x%x", getName(), this, ret);
		if(dataMem)
        {
            if ( dataMemPrepared )
                dataMem->complete();
			dataMem->release();
            dataMem = NULL;
        }
        
		if(countMem)
        {
            if ( countMemPrepared )
                countMem->complete();
			countMem->release();
            countMem = NULL;
        }
        
		if(pb)
			IOFree(pb, sizeof(*pb) + frameLen);
		DecrementOutstandingIO();
        release();
    }
	
	if ( ret != kIOReturnSuccess )
		USBLog(5, "%s[%p]::-DoIsochPipeAsync err 0x%x", getName(), this, ret);
    return ret;
}



IOReturn 
IOUSBInterfaceUserClient::DoLowLatencyIsochPipeAsync(OSAsyncReference asyncRef, IOUSBLowLatencyIsocStruct *isocInfo, IODirection direction)
{
    IOReturn							ret;
    IOUSBPipe *							pipeObj;
    IOUSBLowLatencyIsocCompletion		tap;
    IOMemoryDescriptor *				aDescriptor		= NULL;
    IOUSBLowLatencyIsocFrame *			pFrameList 		= NULL;
    IOUSBLowLatencyCommand *			command 		= NULL;
    IOUSBLowLatencyUserClientBufferInfo *	dataBuffer		= NULL;
    IOUSBLowLatencyUserClientBufferInfo *	frameListDataBuffer	= NULL;
	
    USBLog(7, "+%s[%p]::DoLowLatencyIsochPipeAsync", getName(), this);
    retain();
    IncrementOutstandingIO();		// to make sure LowLatencyIsoReqComplete is still around
    
    if (fOwner && !isInactive())
    {
		pipeObj = GetPipeObj(isocInfo->fPipe);
		if(pipeObj)
		{
			do {
                // First, attempt to get a command for our transfer
                //
                command = (IOUSBLowLatencyCommand *) fFreeUSBLowLatencyCommandPool->getCommand(false);
                
                // If we couldn't get a command, increase the allocation and try again
                //
                if ( command == NULL )
                {
                    IncreaseCommandPool();
                    
                    command = (IOUSBLowLatencyCommand *) fFreeUSBLowLatencyCommandPool->getCommand(false);
                    if ( command == NULL )
                    {
                        USBLog(3,"%s[%p]::DoLowLatencyIsochPipeAsync Could not get a IOUSBLowLatencyIsocCommand",getName(),this);
                        ret = kIOReturnNoResources;
                        break;
                    }
                }
                
                USBLog(6,"%s[%p]::DoLowLatencyIsochPipeAsync: dataBuffer cookie: %ld, offset: %ld, frameList cookie: %ld, offset : %ld", getName(),this, isocInfo->fDataBufferCookie, isocInfo->fDataBufferOffset, isocInfo->fFrameListBufferCookie, isocInfo->fFrameListBufferOffset );
                
                // Find the buffer corresponding to the data buffer cookie:
                //
                dataBuffer = FindBufferCookieInList(isocInfo->fDataBufferCookie);
                
                if ( dataBuffer == NULL )
				{
					USBLog(3,"%s[%p]::DoLowLatencyIsochPipeAsync: Could not find our buffer (cookie %ld) in the list", getName(),this, isocInfo->fDataBufferCookie );
					ret = kIOReturnNoMemory;
					break;
				}
                
                USBLog(6,"%s[%p]::DoLowLatencyIsochPipeAsync: Found data buffer for cookie: %ld, descriptor: %p, uhciDescriptor: %p, offset : %ld", getName(),this, isocInfo->fDataBufferCookie, 
					   dataBuffer->bufferDescriptor, dataBuffer->writeDescritporForUHCI,isocInfo->fDataBufferOffset );
				
                // Create a new IOMD that is a subrange of our data buffer memory descriptor, and prepare it
                //
                aDescriptor = IOMemoryDescriptor::withSubRange( dataBuffer->bufferDescriptor == NULL ? dataBuffer->writeDescritporForUHCI : dataBuffer->bufferDescriptor, isocInfo->fDataBufferOffset, isocInfo->fBufSize, direction );
                if ( aDescriptor == NULL )
				{
					USBLog(3,"%s[%p]::DoLowLatencyIsochPipeAsync: Could not create an IOMD:withSubRange", getName(),this );
					ret = kIOReturnNoMemory;
					break;
				}
				
                // Prepare this descriptor
                //
                ret = aDescriptor->prepare();
                if (ret != kIOReturnSuccess)
                {
 					USBLog(3,"%s[%p]::DoLowLatencyIsochPipeAsync: Preparing the descriptor returned 0x%x", getName(),this, ret );
					break;
                }
                
                // Find the buffer corresponding to the frame list cookie:
                //
                frameListDataBuffer = FindBufferCookieInList(isocInfo->fFrameListBufferCookie);
                
                if ( frameListDataBuffer == NULL )
				{
					USBLog(3,"%s[%p]::DoLowLatencyIsochPipeAsync: Could not find our buffer (cookie %ld) in the list, returning kIOReturnNoMemory", getName(),this, isocInfo->fFrameListBufferCookie );
					ret = kIOReturnNoMemory;
					break;
				}
                
                USBLog(7,"%s[%p]::DoLowLatencyIsochPipeAsync: Found frameList buffer for cookie: %ld, descriptor: %p, offset : %ld", getName(),this, isocInfo->fFrameListBufferCookie, 
					   (void *) frameListDataBuffer->frameListKernelAddress,isocInfo->fFrameListBufferOffset );

                // Get our virtual address by looking at the buffer data and adding in the offset that was passed in
                //
                pFrameList = (IOUSBLowLatencyIsocFrame *) ( (UInt32) frameListDataBuffer->frameListKernelAddress + isocInfo->fFrameListBufferOffset);
                
                // Copy the data into our command buffer
                //
                command->SetAsyncReference( asyncRef );
                command->SetFrameBase( (void *) ((UInt32) frameListDataBuffer->bufferAddress + isocInfo->fFrameListBufferOffset));
                command->SetDataBuffer( aDescriptor );
                
                // Populate our completion routine
                //
                tap.target = this;
				tap.action = &LowLatencyIsoReqComplete;
				tap.parameter = command;
                
				if ( direction == kIODirectionOut )
					ret = pipeObj->Write(aDescriptor, isocInfo->fStartFrame, isocInfo->fNumFrames, pFrameList, &tap, isocInfo->fUpdateFrequency);
				else
					ret = pipeObj->Read(aDescriptor, isocInfo->fStartFrame, isocInfo->fNumFrames,pFrameList, &tap, isocInfo->fUpdateFrequency);
				
            } while (false);
            
			pipeObj->release();
		}
		else
			ret = kIOUSBUnknownPipeErr;
    }
    else
        ret = kIOReturnNotAttached;
	
    if(kIOReturnSuccess != ret) 
    {
		USBLog(3, "%s[%p]::DoLowLatencyIsochPipeAsync err 0x%x", getName(), this, ret);
		
        if ( aDescriptor )
        {
            aDescriptor->release();
            aDescriptor = NULL;
        }
		
        // return command
        //
        if ( command )
            fFreeUSBLowLatencyCommandPool->returnCommand(command);
        
		DecrementOutstandingIO();
        release();
    }
	
    USBLog(7, "-%s[%p]::DoLowLatencyIsochPipeAsync", getName(), this);
    return ret;
}



IOReturn 
IOUSBInterfaceUserClient::ReadIsochPipe(OSAsyncReference asyncRef, IOUSBIsocStruct *stuff, UInt32 sizeIn)
{
    return DoIsochPipeAsync(asyncRef, stuff, kIODirectionIn);
}



IOReturn 
IOUSBInterfaceUserClient::WriteIsochPipe(OSAsyncReference asyncRef, IOUSBIsocStruct *stuff, UInt32 sizeIn)
{
    return DoIsochPipeAsync(asyncRef, stuff, kIODirectionOut);
}

IOReturn 
IOUSBInterfaceUserClient::LowLatencyReadIsochPipe(OSAsyncReference asyncRef, IOUSBLowLatencyIsocStruct *stuff, UInt32 sizeIn)
{
    return DoLowLatencyIsochPipeAsync(asyncRef, stuff, kIODirectionIn);
}



IOReturn 
IOUSBInterfaceUserClient::LowLatencyWriteIsochPipe(OSAsyncReference asyncRef, IOUSBLowLatencyIsocStruct *stuff, UInt32 sizeIn)
{
    return DoLowLatencyIsochPipeAsync(asyncRef, stuff, kIODirectionOut);
}


/*
 * Async version of read pipe - the buffer isn't mapped (yet) into the kernel task
 */
IOReturn
IOUSBInterfaceUserClient::AsyncReadPipe(OSAsyncReference asyncRef, UInt32 pipe, void *buf, UInt32 size, UInt32 noDataTimeout, UInt32 completionTimeout)
{
    IOReturn 			ret;
    IOUSBPipe 			*pipeObj;
    IOUSBCompletion		tap;
    IOMemoryDescriptor *	mem = NULL;
    IOUSBInterfaceUserClientAsyncParamBlock * 			pb = NULL;

    USBLog(7, "+%s[%p]::AsyncReadPipe", getName(), this);
    retain();
    IncrementOutstandingIO();		// to make sure ReqComplete is still around
    
    if (fOwner && !isInactive())
    {
	pipeObj = GetPipeObj(pipe);
	if(pipeObj)
	{
	    do {
		mem = IOMemoryDescriptor::withAddress((vm_address_t)buf, size, kIODirectionIn, fTask);
		if(!mem) 
		{
		    ret = kIOReturnNoMemory;
		    break;
		}
		ret = mem->prepare();
		if(ret != kIOReturnSuccess)
		    break;
	
		pb = (IOUSBInterfaceUserClientAsyncParamBlock *)IOMalloc(sizeof(IOUSBInterfaceUserClientAsyncParamBlock));
		if(!pb) 
		{
		    ret = kIOReturnNoMemory;
		    break;
		}
	
		bcopy(asyncRef, pb->fAsyncRef, sizeof(OSAsyncReference));
		pb->fMax = size;
		pb->fMem = mem;
		tap.target = this;
		tap.action = &ReqComplete;
		tap.parameter = pb;
		ret = pipeObj->Read(mem, noDataTimeout, completionTimeout, &tap, NULL);
	    } while (false);
	    pipeObj->release();
	}
	else
	    ret = kIOUSBUnknownPipeErr;
    }
    else
        ret = kIOReturnNotAttached;
    
    if(ret != kIOReturnSuccess) 
    {
	USBLog(3, "%s[%p]::AsyncReadPipe err 0x%x", getName(), this, ret);
	if(mem) 
	{
	    mem->complete();
	    mem->release();
            mem = NULL;
	}
	if(pb)
	    IOFree(pb, sizeof(*pb));
	
        DecrementOutstandingIO();
        release();
    }
    return ret;
}



/*
 * Async version of write pipe - the buffer isn't mapped (yet) into the kernel task
 */
IOReturn
IOUSBInterfaceUserClient::AsyncWritePipe(OSAsyncReference asyncRef, UInt32 pipe, void *buf, UInt32 size, UInt32 noDataTimeout, UInt32 completionTimeout)
{
    IOReturn 			ret;
    IOUSBPipe 			*pipeObj;
    IOUSBCompletion		tap;
    IOMemoryDescriptor *	mem = NULL;
    IOUSBInterfaceUserClientAsyncParamBlock * 			pb = NULL;

    USBLog(7, "+%s[%p]::AsyncWritePipe", getName(), this);
    retain();
    IncrementOutstandingIO();		// to make sure ReqComplete is still around

    if (fOwner && !isInactive())
    {
	pipeObj = GetPipeObj(pipe);
	if(pipeObj)
	{
	    do {
		mem = IOMemoryDescriptor::withAddress((vm_address_t)buf, size, kIODirectionOut, fTask);
		if(!mem) 
		{
		    ret = kIOReturnNoMemory;
		    break;
		}
		ret = mem->prepare();
		if(ret != kIOReturnSuccess)
		    break;
	
		pb = (IOUSBInterfaceUserClientAsyncParamBlock *)IOMalloc(sizeof(IOUSBInterfaceUserClientAsyncParamBlock));
		if(!pb) 
		{
		    ret = kIOReturnNoMemory;
		    break;
		}
	
		bcopy(asyncRef, pb->fAsyncRef, sizeof(OSAsyncReference));
		pb->fMax = size;
		pb->fMem = mem;
		tap.target = this;
		tap.action = &ReqComplete;
		tap.parameter = pb;
		ret = pipeObj->Write(mem, noDataTimeout, completionTimeout, &tap);
	    } while (false);
	
	    pipeObj->release();
	}
	else
	    ret = kIOUSBUnknownPipeErr;
    }
    else
        ret = kIOReturnNotAttached;
    
    if(ret != kIOReturnSuccess) 
    {
	USBLog(3, "%s[%p]::AsyncWritePipe err 0x%x", getName(), this, ret);
	if(mem) 
	{
	    mem->complete();
	    mem->release();
            mem = NULL;
	}
	if(pb)
	    IOFree(pb, sizeof(*pb));
            
	DecrementOutstandingIO();
        release();
    }
    return ret;
}


//
// stop
// 
// This IOService method is called AFTER we have closed our provider, assuming that the provider was 
// ever opened. If we issue I/O to the provider, then we must have it open, and we will not close
// our provider until all of that I/O is completed.
void 
IOUSBInterfaceUserClient::stop(IOService * provider)
{
    
    USBLog(7, "+%s[%p]::stop(%p), IO: %ld", getName(), this, provider, fOutstandingIO);

    // If we have any kernelDataBuffer pointers, then release them now
    //
    if (fUserClientBufferInfoListHead != NULL)
    {
        ReleasePreparedDescriptors();
    }

    super::stop(provider);

    USBLog(7, "-%s[%p]::stop(%p)", getName(), this, provider);

}

void 
IOUSBInterfaceUserClient::free()
{
    IOReturn ret;

    USBLog(7, "IOUSBInterfaceUserClient[%p]::free", this);
    
    // If we have any kernelDataBuffer pointers, then release them now
    //
    if (fUserClientBufferInfoListHead != NULL)
    {
        ReleasePreparedDescriptors();
    }
    
    if ( fFreeUSBLowLatencyCommandPool )
    {
        fFreeUSBLowLatencyCommandPool->release();
        fFreeUSBLowLatencyCommandPool = NULL;
    }

    if (fGate)
    {
        if (fWorkLoop)
        {
            ret = fWorkLoop->removeEventSource(fGate);
            fWorkLoop->release();
            fWorkLoop = NULL;
        }

        fGate->release();
        fGate = NULL;
    }
        
    //  This needs to be the LAST thing we do, as it disposes of our "fake" member
    //  variables.
    //
    if (fIOUSBInterfaceUserClientExpansionData)
    {
        IOFree(fIOUSBInterfaceUserClientExpansionData, sizeof(IOUSBInterfaceUserClientExpansionData));
        fIOUSBInterfaceUserClientExpansionData = NULL;
    }

    super::free();
}


bool 
IOUSBInterfaceUserClient::finalize( IOOptionBits options )
{
    bool ret;

    USBLog(7, "+%s[%p]::finalize(%08x)", getName(), this, (int)options);
    
    ret = super::finalize(options);
    
    USBLog(7, "-%s[%p]::finalize(%08x) - returning %s", getName(), this, (int)options, ret ? "true" : "false");
    return ret;
}


bool
IOUSBInterfaceUserClient::willTerminate( IOService * provider, IOOptionBits options )
{
    IOUSBPipe 		*pipe = NULL;
    IOReturn		ret;
    UInt32		ioPending = 0;

    // this method is intended to be used to stop any pending I/O and to make sure that
    // we have begun getting our callbacks in order. by the time we get here, the
    // isInactive flag is set, so we really are marked as being done. we will do in here
    // what we used to do in the message method (this happens first)

    USBLog(3, "%s[%p]::willTerminate isInactive = %d", getName(), this, isInactive());

    //  We have seen cases where our fOwner is not valid at this point.  This is strange
    //  but we'll code defensively and only execute if our provider (fOwner) is still around
    //
    if ( fOwner )
    {
        IncrementOutstandingIO();

        ioPending = GetOutstandingIO();
        
        if ( (ioPending > 1) && fOwner )
        {
            int		i;

            USBLog(7, "%s[%p]::willTerminate - outstanding IO(%ld), aborting pipes", getName(), this, ioPending);
            for (i=1; i <= kUSBMaxPipes; i++)
            {
                pipe = fOwner->GetPipeObj(i-1);

                if(pipe)
                {
                    pipe->retain();
                    ret =  pipe->Abort();
                    pipe->release();
                }
            }

        }
        
        DecrementOutstandingIO();
    }

    return super::willTerminate(provider, options);
}


bool
IOUSBInterfaceUserClient::didTerminate( IOService * provider, IOOptionBits options, bool * defer )
{
    // this method comes at the end of the termination sequence. Hopefully, all of our outstanding IO is complete
    // in which case we can just close our provider and IOKit will take care of the rest. Otherwise, we need to 
    // hold on to the device and IOKit will terminate us when we close it later
   USBLog(3, "%s[%p]::didTerminate isInactive = %d, outstandingIO = %ld", getName(), this, isInactive(), fOutstandingIO);

    if ( fOwner )
    {
        if ( fOutstandingIO == 0 )
		{
            fOwner->close(this);
			if ( isInactive() )
				fOwner = NULL;
		}
        else
            fNeedToClose = true;
    }
    
    return super::didTerminate(provider, options, defer);
}


void
IOUSBInterfaceUserClient::DecrementOutstandingIO(void)
{
    if (!fGate)
    {
	if (!--fOutstandingIO && fNeedToClose)
	{
	    USBLog(3, "%s[%p]::DecrementOutstandingIO isInactive = %d, outstandingIO = %ld - closing device", getName(), this, isInactive(), fOutstandingIO);
	    if (fOwner) 
		{
			fOwner->close(this);
			if ( isInactive() )
				fOwner = NULL;
		}
		
            if ( fDead) release();
	}
	return;
    }
    fGate->runAction(ChangeOutstandingIO, (void*)-1);
}


void
IOUSBInterfaceUserClient::IncrementOutstandingIO(void)
{
    if (!fGate)
    {
	fOutstandingIO++;
	return;
    }
    fGate->runAction(ChangeOutstandingIO, (void*)1);
}


IOReturn
IOUSBInterfaceUserClient::ChangeOutstandingIO(OSObject *target, void *param1, void *param2, void *param3, void *param4)
{
    IOUSBInterfaceUserClient *me = OSDynamicCast(IOUSBInterfaceUserClient, target);
    UInt32	direction = (UInt32)param1;
    
    if (!me)
    {
		USBLog(1, "IOUSBInterfaceUserClient::ChangeOutstandingIO - invalid target");
		return kIOReturnSuccess;
    }
	
    switch (direction)
    {
		case 1:
			me->fOutstandingIO++;
			break;
			
		case -1:
			if (!--me->fOutstandingIO && me->fNeedToClose)
            {
                USBLog(6, "%s[%p]::ChangeOutstandingIO isInactive = %d, outstandingIO = %ld - closing device", me->getName(), me, me->isInactive(), me->fOutstandingIO);
                if (me->fOwner) 
				{
					me->fOwner->close(me);
					if ( me->isInactive() )
						me->fOwner = NULL;
				}
				
                if ( me->fDead) 
					me->release();
			}
			break;
			
		default:
			USBLog(1, "%s[%p]::ChangeOutstandingIO - invalid direction", me->getName(), me);
    }
    return kIOReturnSuccess;
}


UInt32
IOUSBInterfaceUserClient::GetOutstandingIO()
{
    UInt32	count = 0;
    
    if (!fGate)
    {
	return fOutstandingIO;
    }
    
    fGate->runAction(GetGatedOutstandingIO, (void*)&count);
    
    return count;
}

IOReturn
IOUSBInterfaceUserClient::GetGatedOutstandingIO(OSObject *target, void *param1, void *param2, void *param3, void *param4)
{
    IOUSBInterfaceUserClient *me = OSDynamicCast(IOUSBInterfaceUserClient, target);

    if (!me)
    {
	USBLog(1, "IOUSBInterfaceUserClient::GetGatedOutstandingIO - invalid target");
	return kIOReturnSuccess;
    }

    *(UInt32 *) param1 = me->fOutstandingIO;

    return kIOReturnSuccess;
}

void
IOUSBInterfaceUserClient::IncreaseCommandPool(void)
{
    int i;
    
    USBLog(3,"%s[%p] Adding (%d) to Command Pool", getName(), this, kSizeToIncrementLowLatencyCommandPool);

    for (i = 0; i < kSizeToIncrementLowLatencyCommandPool; i++)
    {
        IOUSBLowLatencyCommand *command = IOUSBLowLatencyCommand::NewCommand();
        if (command)
            fFreeUSBLowLatencyCommandPool->returnCommand(command);
    }
    
    fCurrentSizeOfCommandPool += kSizeToIncrementLowLatencyCommandPool;

}

void
IOUSBInterfaceUserClient::ReleasePreparedDescriptors(void)
{
    IOUSBLowLatencyUserClientBufferInfo *	kernelDataBuffer;
    IOUSBLowLatencyUserClientBufferInfo *	nextBuffer;

    if ( fOutstandingIO != 0 )
    {
        USBLog(5, "+IOUSBInterfaceUserClient[%p]::ReleasePreparedDescriptors: OutstandingIO is NOT 0 (%ld) ", this, fOutstandingIO);
        return;
    }
    // If we have any kernelDataBuffer pointers, then release them now
    //
    if (fUserClientBufferInfoListHead != NULL)
    {
        //USBLog(5, "+%s[%p]::ReleasePreparedDescriptors: fUserClientBufferInfoListHead NOT NULL (%p) ", getName(), this, fUserClientBufferInfoListHead);
    
        nextBuffer = fUserClientBufferInfoListHead;
        kernelDataBuffer = fUserClientBufferInfoListHead;
        
        // Traverse the list and release memory
        //
        while ( nextBuffer != NULL )
        {
            nextBuffer = kernelDataBuffer->nextBuffer;

            // Now, need to complete/release/free the objects we allocated in our prepare
            //
            if ( kernelDataBuffer->frameListMap )
            {
                USBLog(3, "+IOUSBInterfaceUserClient[%p]::ReleasePreparedDescriptors releasing frameListMap (@ 0x%x)", this, kernelDataBuffer->frameListKernelAddress);
                kernelDataBuffer->frameListMap->release();
                kernelDataBuffer->frameListMap = NULL;
                kernelDataBuffer->frameListKernelAddress = NULL;
                
            }
                
            if ( kernelDataBuffer->frameListDescriptor )
            {
                kernelDataBuffer->frameListDescriptor->complete();
                kernelDataBuffer->frameListDescriptor->release();
                kernelDataBuffer->frameListDescriptor = NULL;
            }

            if ( kernelDataBuffer->bufferDescriptor )
            {
                // We call prepare on the bufferDescriptor, so we need to call complete on it 
                //
                kernelDataBuffer->bufferDescriptor->complete();
                kernelDataBuffer->bufferDescriptor->release();
                kernelDataBuffer->bufferDescriptor = NULL;
           }
            
			if ( kernelDataBuffer->writeDescritporForUHCI )
			{
				kernelDataBuffer->writeDescritporForUHCI->complete();
				kernelDataBuffer->writeDescritporForUHCI->release();
				kernelDataBuffer->writeDescritporForUHCI = NULL;
			}
			
			if ( kernelDataBuffer->writeMapForUHCI )
			{
				USBLog(3, "+IOUSBInterfaceUserClient[%p]::ReleasePreparedDescriptors releasing uhciMap (%p)", this, kernelDataBuffer->writeMapForUHCI);
				kernelDataBuffer->writeMapForUHCI->release();
				kernelDataBuffer->writeMapForUHCI = NULL;
			}
            // Finally, deallocate our kernelDataBuffer
            //
            IOFree(kernelDataBuffer, sizeof(IOUSBLowLatencyUserClientBufferInfo));
            
            kernelDataBuffer = nextBuffer;
        }
        
        fUserClientBufferInfoListHead = NULL;
    }
}

// padding methods
//
OSMetaClassDefineReservedUnused(IOUSBInterfaceUserClient,  0);
OSMetaClassDefineReservedUnused(IOUSBInterfaceUserClient,  1);
OSMetaClassDefineReservedUnused(IOUSBInterfaceUserClient,  2);
OSMetaClassDefineReservedUnused(IOUSBInterfaceUserClient,  3);
OSMetaClassDefineReservedUnused(IOUSBInterfaceUserClient,  4);
OSMetaClassDefineReservedUnused(IOUSBInterfaceUserClient,  5);
OSMetaClassDefineReservedUnused(IOUSBInterfaceUserClient,  6);
OSMetaClassDefineReservedUnused(IOUSBInterfaceUserClient,  7);
OSMetaClassDefineReservedUnused(IOUSBInterfaceUserClient,  8);
OSMetaClassDefineReservedUnused(IOUSBInterfaceUserClient,  9);
OSMetaClassDefineReservedUnused(IOUSBInterfaceUserClient, 10);
OSMetaClassDefineReservedUnused(IOUSBInterfaceUserClient, 11);
OSMetaClassDefineReservedUnused(IOUSBInterfaceUserClient, 12);
OSMetaClassDefineReservedUnused(IOUSBInterfaceUserClient, 13);
OSMetaClassDefineReservedUnused(IOUSBInterfaceUserClient, 14);
OSMetaClassDefineReservedUnused(IOUSBInterfaceUserClient, 15);
OSMetaClassDefineReservedUnused(IOUSBInterfaceUserClient, 16);
OSMetaClassDefineReservedUnused(IOUSBInterfaceUserClient, 17);
OSMetaClassDefineReservedUnused(IOUSBInterfaceUserClient, 18);
OSMetaClassDefineReservedUnused(IOUSBInterfaceUserClient, 19);



IOUSBLowLatencyCommand *
IOUSBLowLatencyCommand::NewCommand()
{
    IOUSBLowLatencyCommand *me = new IOUSBLowLatencyCommand;
    
    return me;

}

void  			
IOUSBLowLatencyCommand::SetAsyncReference(OSAsyncReference  ref)
{
    bcopy(ref, fAsyncRef, sizeof(OSAsyncReference));
}

// padding methods
//
OSMetaClassDefineReservedUnused(IOUSBLowLatencyCommand,  0);
OSMetaClassDefineReservedUnused(IOUSBLowLatencyCommand,  1);
OSMetaClassDefineReservedUnused(IOUSBLowLatencyCommand,  2);
OSMetaClassDefineReservedUnused(IOUSBLowLatencyCommand,  3);
OSMetaClassDefineReservedUnused(IOUSBLowLatencyCommand,  4);
OSMetaClassDefineReservedUnused(IOUSBLowLatencyCommand,  5);
OSMetaClassDefineReservedUnused(IOUSBLowLatencyCommand,  6);
OSMetaClassDefineReservedUnused(IOUSBLowLatencyCommand,  7);
OSMetaClassDefineReservedUnused(IOUSBLowLatencyCommand,  8);
OSMetaClassDefineReservedUnused(IOUSBLowLatencyCommand,  9);
OSMetaClassDefineReservedUnused(IOUSBLowLatencyCommand, 10);
OSMetaClassDefineReservedUnused(IOUSBLowLatencyCommand, 11);
OSMetaClassDefineReservedUnused(IOUSBLowLatencyCommand, 12);
OSMetaClassDefineReservedUnused(IOUSBLowLatencyCommand, 13);
OSMetaClassDefineReservedUnused(IOUSBLowLatencyCommand, 14);
OSMetaClassDefineReservedUnused(IOUSBLowLatencyCommand, 15);
OSMetaClassDefineReservedUnused(IOUSBLowLatencyCommand, 16);
OSMetaClassDefineReservedUnused(IOUSBLowLatencyCommand, 17);
OSMetaClassDefineReservedUnused(IOUSBLowLatencyCommand, 18);
OSMetaClassDefineReservedUnused(IOUSBLowLatencyCommand, 19);