/*
 * Copyright (c) 2004-2006 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.2 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * http://www.apple.com/publicsource and read it before using this file.
 * 
 * This 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@
 */


#include <kern/clock.h>
#include <machine/limits.h>

#include <IOKit/usb/IOUSBRootHubDevice.h>
#include <IOKit/usb/IOUSBLog.h>

#include "AppleUSBUHCI.h"
#include "AppleUHCIListElement.h"


static char *
USBErrorToString(IOReturn status)
{
    switch (status) {
        case kIOReturnSuccess:
            return "kIOReturnSuccess";
        case kIOReturnError:
            return "kIOReturnError";
        case kIOReturnNotResponding:
            return "kIOReturnNotResponding";
        case kIOUSBPipeStalled:
            return "kIOUSBPipeStalled";
        case kIOReturnOverrun:
            return "kIOReturnOverrun";
        case kIOReturnUnderrun:
            return "kIOReturnUnderrun";
        case kIOUSBLinkErr:
            return "kIOUSBLinkErr";
        case kIOUSBCRCErr:
            return "kIOUSBCRCErr";
        case kIOUSBBitstufErr:
            return "kIOUSBBitstufErr";
        case kIOUSBTransactionReturned:
            return "kIOUSBTransactionReturned";
        case kIOReturnAborted:
            return "kIOReturnAborted";
        case kIOReturnIsoTooNew:
            return "kIOReturnIsoTooNew";
        case kIOReturnIsoTooOld:
            return "kIOReturnIsoTooOld";
        case kIOReturnNoDevice:
            return "kIOReturnNoDevice";
        case kIOReturnBadArgument:
            return "kIOReturnBadArgument";
        case kIOReturnInternalError:
            return "kIOReturnInternalError";
        case kIOReturnNoMemory:
            return "kIOReturnNoMemory";
        case kIOReturnUnsupported:
            return "kIOReturnUnsupported";
        case kIOReturnNoResources:
            return "kIOReturnNoResources";
        case kIOReturnNoBandwidth:
            return "kIOReturnNoBandwidth";
        case kIOReturnIPCError:
            return "kIOReturnIPCError";
        case kIOReturnTimeout:
            return "kIOReturnTimeout";
        case kIOReturnBusy:
            return "kIOReturnBusy";
        case kIOUSBTransactionTimeout:
            return "kIOUSBTransactionTimeout";
        case kIOUSBNotSent1Err:
            return "kIOUSBNotSent1Err";
        case kIOUSBNotSent2Err:
            return "kIOUSBNotSent2Err";
    }
    return "Unknown";
}


/*
 * UIM methods
 */
// ========================================================================
#pragma mark Control
// ========================================================================

IOReturn
AppleUSBUHCI::UIMCreateControlEndpoint(
                                       UInt8				functionNumber,
                                       UInt8				endpointNumber,
                                       UInt16				maxPacketSize,
                                       UInt8				speed,
                                       USBDeviceAddress    		highSpeedHub,
                                       int			        highSpeedPort)
{
    return UIMCreateControlEndpoint(functionNumber, endpointNumber, maxPacketSize, speed);
}



IOReturn
AppleUSBUHCI::UIMCreateControlEndpoint(UInt8				functionNumber,
                                       UInt8				endpointNumber,
                                       UInt16				maxPacketSize,
                                       UInt8				speed)
{
    AppleUHCIQueueHead					*pQH;
    AppleUHCIQueueHead					*prevQH;
	AppleUHCITransferDescriptor			*pTD;
    
    
    if (functionNumber == _rootFunctionNumber) 
	{
        return kIOReturnSuccess;
    }
    
    USBLog(3, "AppleUSBUHCI[%p]::UIMCreateControlEndpoint (f %d ep %d) max %d spd %d", this, functionNumber, endpointNumber, maxPacketSize, speed);

    if (maxPacketSize == 0) 
	{
		USBLog(1, "AppleUSBUHCI[%p]::UIMCreateControlEndpoint - maxPacketSize 0 is illegal (kIOReturnBadArgument)", this);
        return kIOReturnBadArgument;
    }
    
    USBLog(5, "AppleUSBUHCI[%p]::UIMCreateControlEndpoint allocating endpoint", this);
    pQH = AllocateQH(functionNumber, endpointNumber, kUSBNone, speed, maxPacketSize, kUSBControl);
    
    if (pQH == NULL)
        return kIOReturnNoMemory;
    
	pTD = AllocateTD(pQH);
	if (!pTD)
	{
		DeallocateQH(pQH);
		return kIOReturnNoMemory;
	}

	// this is a dummy TD which will be filled in when we create a transfer
	pTD->GetSharedLogical()->ctrlStatus = 0;						// make sure it is inactive
	pQH->firstTD = pTD;
	pQH->lastTD = pTD;
	pQH->GetSharedLogical()->elink = HostToUSBLong(pTD->GetPhysicalAddrWithType());
	
    // Now link the endpoint's queue head into the schedule
    if (speed == kUSBDeviceSpeedLow) 
	{
        prevQH = _lsControlQHEnd;
    } else 
	{
        prevQH = _fsControlQHEnd;
    }
    USBLog(5, "AppleUSBUHCI[%p]::UIMCreateControlEndpoint linking qh %p into schedule after %p", this, pQH, prevQH);
	
    pQH->_logicalNext = prevQH->_logicalNext;
    pQH->SetPhysicalLink(prevQH->GetPhysicalLink());
    IOSync();
    
    prevQH->_logicalNext = pQH;
    prevQH->SetPhysicalLink(pQH->GetPhysicalAddrWithType());
    IOSync();
    if (speed == kUSBDeviceSpeedLow) 
	{
        _lsControlQHEnd = pQH;
    } else 
	{
        _fsControlQHEnd = pQH;
    }
    
    USBLog(3, "AppleUSBUHCI[%p]::UIMCreateControlEndpoint done pQH %p FN %d EP %d MPS %d", this, pQH, pQH->functionNumber, pQH->endpointNumber, pQH->maxPacketSize);

    return kIOReturnSuccess;
}



IOReturn
AppleUSBUHCI::UIMCreateControlTransfer(short				functionNumber,
                                       short				endpointNumber,
                                       IOUSBCommand*		command,
                                       IOMemoryDescriptor	*CBP,
                                       bool					bufferRounding,			// short packet OK
                                       UInt32				bufferSize,
                                       short				direction)
{
    AppleUHCIQueueHead					*pQH;
    AppleUHCITransferDescriptor			*td, *last_td;
    IOReturn							status;
    
    USBLog(7, "AppleUSBUHCI[%p]::UIMCreateControlTransfer (f %d ep %d dir %d) size %d", this, functionNumber, endpointNumber, direction, (int)bufferSize);
    
    pQH = FindQueueHead(functionNumber, endpointNumber, kUSBAnyDirn, kUSBControl);
    if (pQH == NULL) 
	{
        USBLog(2, "AppleUSBUHCI[%p]::UIMCreateControlTransfer - queue head not found", this);
        return kIOUSBEndpointNotFound;
    }
    
    if (pQH->stalled) 
	{
		if (endpointNumber == 0)
		{
			USBError(1, "AppleUSBUHCI[%p]::UIMCreateControlTransfer - address %d endpoint 0 is incorrectly marked as stalled!", this, functionNumber);
		}
		else
		{
			USBLog(2, "AppleUSBUHCI[%p]::UIMCreateControlTransfer - Control pipe for ep %d stalled", this, endpointNumber);
			return kIOUSBPipeStalled;
		}
    }
    
	// control transactions may be coming from sources other than the device driver (e.g. Prober)
	// so restart the bus and let them through.. we will start it up again later
	if ( _idleSuspend )
	{
		USBLog(3, "AppleUSBUHCI[%p]::UIMCreateControlTransfer - in _idleSuspend - restarting bus",  this);
		setPowerState(kUHCIPowerLevelRunning, this);
	}

    // Here's how we will assemble the transaction:
	// There are up to three parts to a control transaction.  If the command says
	// that it's a multi-part transaction, and this is not the last part of the transaction,
	// assemble the parts in a queue but don't start it yet.
    
    USBLog(7, "AppleUSBUHCI[%p]::UIMCreateControlTransfer - allocating TD chain", this);
    status = AllocTDChain(pQH, command, CBP, bufferSize, direction, true);
    if (status != kIOReturnSuccess) 
	{
		USBLog(2, "AppleUSBUHCI[%p]::UIMCreateControlTransfer - returning status %p", this, (void*)status);
    }
    	
	USBLog(7, "AppleUSBUHCI[%p]::UIMCreateControlTransfer - pQH[%p] firstTD[%p] lastTD[%p] status[%p]", this, pQH, pQH->firstTD, pQH->lastTD, (void*)status);
	return status;
}


// ========================================================================
#pragma mark Bulk
// ========================================================================


IOReturn
AppleUSBUHCI::UIMCreateBulkEndpoint(UInt8				functionNumber,
                                    UInt8				endpointNumber,
                                    UInt8				direction,
                                    UInt8				speed,
                                    UInt16				maxPacketSize,
                                    USBDeviceAddress    highSpeedHub,
                                    int			        highSpeedPort)
{
    return UIMCreateBulkEndpoint(functionNumber, endpointNumber, direction, speed, maxPacketSize);
}



IOReturn
AppleUSBUHCI::UIMCreateBulkEndpoint(UInt8 functionNumber, UInt8 endpointNumber, UInt8 direction, UInt8 speed, UInt8	maxPacketSize)
{
    AppleUHCITransferDescriptor			*pTD;
    AppleUHCIQueueHead					*pQH;
    AppleUHCIQueueHead					*prevQH;
    
    USBLog(5, "AppleUSBUHCI[%p]::UIMCreateBulkEndpoint (fn %d ep %d dir %d) speed %d mp %d", this, functionNumber, endpointNumber, direction, speed, maxPacketSize);
    
    if (maxPacketSize == 0) 
	{
		USBLog(1, "AppleUSBUHCI[%p]::UIMCreateBulkEndpoint - maxPacketSize 0 is illegal (kIOReturnBadArgument)", this);
        return kIOReturnBadArgument;
    }
    
    pQH = AllocateQH(functionNumber, endpointNumber, direction, speed, maxPacketSize, kUSBBulk);
    
    if (pQH == NULL) 
	{
        return kIOReturnNoMemory;
    }

    USBLog(7, "AppleUSBUHCI[%p]::UIMCreateBulkEndpoint allocating dummy TD", this);
	pTD = AllocateTD(pQH);
	if (!pTD)
	{
		DeallocateQH(pQH);
		return kIOReturnNoMemory;
	}

	// this is a dummy TD which will be filled in when we create a transfer
	pTD->GetSharedLogical()->ctrlStatus = 0;						// make sure it is inactive
	pQH->firstTD = pTD;
	pQH->lastTD = pTD;
	pQH->GetSharedLogical()->elink = HostToUSBLong(pTD->GetPhysicalAddrWithType());
	
    // Now link the endpoint's queue head into the schedule
    prevQH = _bulkQHEnd;
    pQH->_logicalNext = prevQH->_logicalNext;
    pQH->SetPhysicalLink(prevQH->GetPhysicalLink());
    IOSync();
    
    prevQH->_logicalNext = pQH;
    prevQH->SetPhysicalLink(pQH->GetPhysicalAddrWithType());
    IOSync();
    _bulkQHEnd = pQH;

    return kIOReturnSuccess;
}



IOReturn
AppleUSBUHCI::UIMCreateBulkTransfer(IOUSBCommand* command)
{
    AppleUHCIQueueHead				*pQH = NULL;
    IOMemoryDescriptor				*mp = NULL;
    IOReturn						status = 0xDEADBEEF;
    
    USBLog(7, "AppleUSBUHCI[%p]::UIMCreateBulkTransfer (%d, %d, %d) size %d", this, command->GetAddress(), command->GetEndpoint(), command->GetDirection(), (int)command->GetReqCount());
    
    pQH = FindQueueHead(command->GetAddress(), command->GetEndpoint(), command->GetDirection(), kUSBBulk);
    if (pQH == NULL) 
	{
        USBLog(2, "AppleUSBUHCI[%p]::UIMCreateBulkTransfer - endpoint (fn %d, ep %d, dir %d) not found", this, command->GetAddress(), command->GetEndpoint(), command->GetDirection() );
        return kIOUSBEndpointNotFound;
    }
    
    if (pQH->stalled) 
	{
        USBLog(4, "AppleUSBUHCI[%p]::UIMCreateBulkTransfer - Bulk pipe stalled", this);
        return kIOUSBPipeStalled;
    }
    mp = command->GetBuffer();
	
    status = AllocTDChain(pQH, command, mp, command->GetReqCount(), command->GetDirection(), false);
    if (status != kIOReturnSuccess) 
	{
        USBLog(4, "AppleUSBUHCI[%p]::UIMCreateBulkTransfer - AllocTDChain returns %d", this, status);
        return status;
    }

    return kIOReturnSuccess;
}


// ========================================================================
#pragma mark Interrupt
// ========================================================================

IOReturn
AppleUSBUHCI::UIMCreateInterruptEndpoint(
                                         short				functionNumber,
                                         short				endpointNumber,
                                         UInt8				direction,
                                         short				speed,
                                         UInt16				maxPacketSize,
                                         short				pollingRate,
                                         USBDeviceAddress   highSpeedHub,
                                         int                highSpeedPort)
{
    return UIMCreateInterruptEndpoint(functionNumber, endpointNumber, direction, speed, maxPacketSize, pollingRate);
}



IOReturn
AppleUSBUHCI::UIMCreateInterruptEndpoint(short				functionNumber,
                                         short				endpointNumber,
                                         UInt8				direction,
                                         short				speed,
                                         UInt16				maxPacketSize,
                                         short				pollingRate)
{
    AppleUHCIQueueHead			*pQH, *prevQH;
	AppleUHCITransferDescriptor	*pTD;
    int							i;
    
    USBLog(3, "AppleUSBUHCI[%p]::UIMCreateInterruptEndpoint (fn %d, ep %d, dir %d) spd %d pkt %d rate %d", this, functionNumber, endpointNumber, direction, speed, maxPacketSize, pollingRate );
	
    if (functionNumber == _rootFunctionNumber) 
	{
        if (endpointNumber != 0 && endpointNumber != 1) 
		{
			USBLog(1, "AppleUSBUHCI[%p]::UIMCreateInterruptEndpoint - maxPacketSize 0 is illegal (kIOReturnBadArgument)", this);
            return kIOReturnBadArgument;
        }
        return RHCreateInterruptEndpoint(endpointNumber, direction, speed, maxPacketSize, pollingRate);
    }

    // If the interrupt already exists, then we need to delete it first, as we're probably trying
    // to change the Polling interval via SetPipePolicy().
    //
    pQH = FindQueueHead(functionNumber, endpointNumber, direction, kUSBInterrupt);
    if ( pQH != NULL )
    {
        IOReturn ret;
        USBLog(2, "AppleUSBUHCI[%p]::UIMCreateInterruptEndpoint endpoint already existed -- deleting it", this);
        ret = UIMDeleteEndpoint(functionNumber, endpointNumber, direction);
        if ( ret != kIOReturnSuccess)
        {
            USBLog(1, "AppleUSBUHCI[%p]::UIMCreateInterruptEndpoint deleting endpoint returned %p", this, (void*)ret);
            return ret;
        }
    }

	for (i=kUHCI_NINTR_QHS-1; i>=0; i--) 
	{
        if ((1 << i) <= pollingRate) 
		{
            break;
        }
    }
    if (i<0) 
	{
        i = 0;
    }
    USBLog(5, "AppleUSBUHCI[%p]::UIMCreateInterruptEndpoint - we will use interrupt queue %d, which corresponds to a rate of %d", this, i, (1 << i));
    
    USBLog(7, "AppleUSBUHCI[%p]::UIMCreateInterruptEndpoint allocating endpoint", this);
    pQH = AllocateQH(functionNumber, endpointNumber, direction, speed, maxPacketSize, kUSBInterrupt);
    
    if (pQH == NULL)
        return kIOReturnNoMemory;
    
    USBLog(7, "AppleUSBUHCI[%p]::UIMCreateInterruptEndpoint allocating dummy TD", this);
	pTD = AllocateTD(pQH);
	if (!pTD)
	{
		DeallocateQH(pQH);
		return kIOReturnNoMemory;
	}
	
	// this is a dummy TD which will be filled in when we create a transfer
	pTD->GetSharedLogical()->ctrlStatus = 0;						// make sure it is inactive
	pQH->firstTD = pTD;
	pQH->lastTD = pTD;
	pQH->GetSharedLogical()->elink = HostToUSBLong(pTD->GetPhysicalAddrWithType());
	
    // Now link the endpoint's queue head into the schedule
    prevQH = _intrQH[i];
    pQH->_logicalNext = prevQH->_logicalNext;
    pQH->SetPhysicalLink(prevQH->GetPhysicalLink());
    IOSync();
    
    prevQH->_logicalNext = pQH;
    prevQH->SetPhysicalLink(pQH->GetPhysicalAddrWithType());
	
    USBLog(3, "AppleUSBUHCI[%p]::UIMCreateInterruptEndpoint done pQH[%p]", this, pQH);

    return kIOReturnSuccess;
}



// method in 1.8.2
IOReturn
AppleUSBUHCI::UIMCreateInterruptTransfer(IOUSBCommand* command)
{
    AppleUHCIQueueHead				*pQH;
    IOReturn						status;
    IOMemoryDescriptor				*mp;
    IOByteCount						len;
	
    
    if (command->GetAddress() == _rootFunctionNumber)
    {
        return RHCreateInterruptTransfer(command);
    }
    
    USBLog(7, "AppleUSBUHCI[%p]::UIMCreateInterruptTransfer - adr=(%d,%d) len %d rounding %d", this, command->GetAddress(), command->GetEndpoint(), (int)command->GetReqCount(), command->GetBufferRounding());
    pQH = FindQueueHead(command->GetAddress(), command->GetEndpoint(), command->GetDirection(), kUSBInterrupt);
    if (pQH == NULL) 
	{
		USBLog(1, "AppleUSBUHCI[%p]::UIMCreateInterruptTransfer - QH not found", this);
        return kIOUSBEndpointNotFound;
    }
    
    if (pQH->stalled) 
	{
        USBLog(1, "AppleUSBUHCI[%p]::UIMCreateInterruptTransfer - Interrupt pipe stalled", this);
        return kIOUSBPipeStalled;
    }
        
    mp = command->GetBuffer();
    len = command->GetReqCount();
    
#define INTERRUPT_TRANSFERS_ONE_PACKET 0
#if INTERRUPT_TRANSFERS_ONE_PACKET
    // Restrict interrupt transfers to one packet only.
    // This seems to help Bluetooth USB adapters work.
    if ((int)len > ep->maxPacketSize) 
	{
        len = ep->maxPacketSize;
    }
#endif
    
    status = AllocTDChain(pQH, command, mp, len, command->GetDirection(), false);
    if (status != kIOReturnSuccess) 
	{
        return status;
    }

    USBLog(7, "AppleUSBUHCI[%p]::UIMCreateInterruptTransfer - done", this);
    return kIOReturnSuccess;
}


// ========================================================================
#pragma mark Isochronous
// ========================================================================


IOReturn
AppleUSBUHCI::UIMCreateIsochEndpoint(short				functionNumber,
                                     short				endpointNumber,
                                     UInt32				maxPacketSize,
                                     UInt8				direction,
                                     USBDeviceAddress   highSpeedHub,
                                     int                highSpeedPort)
{
    return UIMCreateIsochEndpoint(functionNumber, endpointNumber, maxPacketSize, direction);
}


IOReturn 
AppleUSBUHCI::UIMCreateIsochEndpoint(short					functionAddress,
									 short					endpointNumber,
									 UInt32					maxPacketSize,
									 UInt8					direction)
{
    IOUSBControllerIsochEndpoint*			pEP;
    UInt32									curMaxPacketSize;
    UInt32									xtraRequest;
	IOReturn								res;
    
	
    USBLog(6, "AppleUSBUHCI[%p]::UIMCreateIsochEndpoint(%d, %d, %ld, %d)", this, functionAddress, endpointNumber, maxPacketSize, direction);
	// see if the endpoint already exists - if so, this is a SetPipePolicy
    pEP = FindIsochronousEndpoint(functionAddress, endpointNumber, direction, NULL);
	
    if (pEP) 
    {
        // this is the case where we have already created this endpoint, and now we are adjusting the maxPacketSize
        //
        USBLog(6,"AppleUSBUHCI[%p]::UIMCreateIsochEndpoint endpoint already exists, attempting to change maxPacketSize to %ld", this, maxPacketSize);
		
        curMaxPacketSize = pEP->maxPacketSize;
        if (maxPacketSize == curMaxPacketSize) 
		{
            USBLog(4, "AppleUSBUHCI[%p]::UIMCreateIsochEndpoint maxPacketSize (%ld) the same, no change", this, maxPacketSize);
            return kIOReturnSuccess;
        }
        if (maxPacketSize > curMaxPacketSize) 
		{
            // client is trying to get more bandwidth
            xtraRequest = maxPacketSize - curMaxPacketSize;
			if (xtraRequest > _isocBandwidth)
			{
				USBLog(1,"AppleUSBUHCI[%p]::UIMCreateIsochEndpoint out of bandwidth, request (extra) = %ld, available: %ld", this, xtraRequest, _isocBandwidth);
				return kIOReturnNoBandwidth;
			}
			_isocBandwidth -= xtraRequest;
			USBLog(5, "AppleUSBUHCI[%p]::UIMCreateIsochEndpoint grabbing additional bandwidth: %ld, new available: %ld", this, xtraRequest, _isocBandwidth);
        } else 
		{
            // client is trying to return some bandwidth
            xtraRequest = curMaxPacketSize - maxPacketSize;
            _isocBandwidth += xtraRequest;
            USBLog(5, "AppleUSBUHCI[%p]::UIMCreateIsochEndpoint returning some bandwidth: %ld, new available: %ld", this, xtraRequest, _isocBandwidth);
        }
        pEP->maxPacketSize = maxPacketSize;
		
		USBLog(6,"AppleUSBUHCI[%p]::UIMCreateIsochEndpoint new size %ld", this, maxPacketSize);
        return kIOReturnSuccess;
    }
	else
		USBLog(7,"AppleUSBUHCI[%p]::UIMCreateIsochEndpoint no endpoint", this);

    
    // we neeed to create a new EP structure
	if (maxPacketSize > _isocBandwidth) 
	{
		USBLog(1,"AppleUSBUHCI[%p]::UIMCreateIsochEndpoint out of bandwidth, request (extra) = %ld, available: %ld", this, maxPacketSize, _isocBandwidth);
		return kIOReturnNoBandwidth;
	}
	
    pEP = CreateIsochronousEndpoint(functionAddress, endpointNumber, direction);

    if (pEP == NULL) 
        return kIOReturnNoMemory;

	pEP->maxPacketSize = maxPacketSize;
	USBLog(5,"AppleUSBUHCI[%p]::UIMCreateIsochEndpoint 2 size %ld", this, maxPacketSize);

    pEP->inSlot = kUHCI_NVFRAMES+1;
	
    _isocBandwidth -= maxPacketSize;
    USBLog(6, "AppleUSBUHCI[%p]::UIMCreateIsochEndpoint success. bandwidth used = %ld, new available: %ld", this, maxPacketSize, _isocBandwidth);
    return kIOReturnSuccess;
}



IOReturn
AppleUSBUHCI::DeleteIsochEP(IOUSBControllerIsochEndpoint* pEP)
{
    IOUSBControllerIsochEndpoint		*curEP, *prevEP;
    UInt32								currentMaxPacketSize;
	
    USBLog(7, "AppleUSBUHCI[%p]::DeleteIsochEP (%p)", this, pEP);
    if (pEP->activeTDs)
    {
		USBLog(6, "AppleUSBUHCI[%p]::DeleteIsochEP- there are still %ld active TDs - aborting", this, pEP->activeTDs);
		AbortIsochEP(pEP);
		if (pEP->activeTDs)
		{
			USBError(1, "AppleUSBUHCI[%p]::DeleteIsochEP- after abort there are STILL %ld active TDs", this, pEP->activeTDs);
		}
    }
    prevEP = NULL;
    curEP = _isochEPList;
    while (curEP)
    {
		if (curEP == pEP)
		{
			if (prevEP)
				prevEP->nextEP = curEP->nextEP;
			else
				_isochEPList = curEP->nextEP;
			break;
		}
		prevEP = curEP;
		curEP = curEP->nextEP;
    }
    
	// Save the current max packet size, as DeallocateIsochBandwidth will set the ep->mps to 0
	currentMaxPacketSize = pEP->maxPacketSize;
	
    // need to return the bandwidth used
	_isocBandwidth += currentMaxPacketSize;
	USBLog(4, "AppleUSBUHCI[%p]::DeleteIsochEP returned bandwidth %ld, new available: %ld", this, currentMaxPacketSize, _isocBandwidth);	
	
	DeallocateIsochEP(pEP);

    return kIOReturnSuccess;
}



IOReturn
AppleUSBUHCI::CreateIsochTransfer(IOUSBControllerIsochEndpoint		*pEP,
								  IOUSBIsocCompletion				completion,
								  UInt64							frameNumberStart,
								  IOMemoryDescriptor				*pBuffer,
								  UInt32							frameCount,
								  IOUSBIsocFrame					*pFrames,
								  UInt32							updateFrequency,
								  bool								lowLatency,
								  bool								requestFromRosettaClient)
{
    AppleUHCIIsochTransferDescriptor		*pNewITD=NULL;
    UInt64									maxOffset;
    UInt64									curFrameNumber = GetFrameNumber();
    UInt64									frameDiff;
    UInt32									diff32;
    IOByteCount								transferOffset;
    IOUSBLowLatencyIsocFrame				*pLLFrames;
    UInt32									bufferSize;
    UInt32									ioc = 0;
    UInt32									i;
    UInt32									pageOffset;
    IOPhysicalAddress						dmaStartAddr;
    IOByteCount								segLen;
	UInt32									myStatFlags;
	UInt32									myDirection;
	AppleUHCIIsochEndpoint					*pUEP = OSDynamicCast(AppleUHCIIsochEndpoint, pEP);
	
    USBLog(7, "AppleUSBUHCI[%p]::CreateIsochTransfer - frameCount %ld - frameNumberStart %Ld - curFrameNumber %Ld", this, frameCount, frameNumberStart, curFrameNumber);
    USBLog(7, "AppleUSBUHCI[%p]::CreateIsochTransfer - updateFrequency %ld - lowLatency %d", this, updateFrequency, lowLatency);
	
	if (!pUEP)
	{
		USBError(1, "AppleUSBUHCI[%p]::CreateIsochTransfer - not a UHCI endpoint", this);
		return kIOReturnBadArgument;
	}
	
    maxOffset = kUHCI_NVFRAMES;
    if (frameNumberStart < pEP->firstAvailableFrame)
    {
		USBLog(3,"AppleUSBUHCI[%p]::CreateIsochTransfer: no overlapping frames -   EP (%p) frameNumberStart: %Ld, pEP->firstAvailableFrame: %Ld.  Returning 0x%x", this, pEP, frameNumberStart, pEP->firstAvailableFrame, kIOReturnIsoTooOld);
		return kIOReturnIsoTooOld;
    }
    pEP->firstAvailableFrame = frameNumberStart;
	
	switch (pEP->direction)
	{
		case kUSBIn:
			myDirection = kUHCI_TD_PID_IN;
			break;
			
		case kUSBOut:
			myDirection = kUHCI_TD_PID_OUT;
			break;
			
		default:
			USBLog(3,"AppleUSBUHCI[%p]::CreateIsochTransfer: bad direction(%d) in pEP[%p]", this, pEP->direction, pEP);
			return kIOReturnBadArgument;
	}
	
	
    if (frameNumberStart <= curFrameNumber)
    {
        if (frameNumberStart < (curFrameNumber - maxOffset))
        {
            USBLog(3,"AppleUSBUHCI[%p]::CreateIsochTransfer request frame WAY too old.  frameNumberStart: %Ld, curFrameNumber: %Ld.  Returning 0x%x", this, frameNumberStart, curFrameNumber, kIOReturnIsoTooOld);
            return kIOReturnIsoTooOld;
        }
        USBLog(5,"AppleUSBUHCI[%p]::CreateIsochTransfer WARNING! curframe later than requested, expect some notSent errors!  frameNumberStart: %Ld, curFrameNumber: %Ld.  USBIsocFrame Ptr: %p, First ITD: %p", this, frameNumberStart, curFrameNumber, pFrames, pEP->toDoEnd);
    } else 
    {					// frameNumberStart > curFrameNumber
        if (frameNumberStart > (curFrameNumber + maxOffset))
        {
            USBLog(3,"AppleUSBUHCI[%p]::CreateIsochTransfer request frame too far ahead!  frameNumberStart: %Ld, curFrameNumber: %Ld", this, frameNumberStart, curFrameNumber);
            return kIOReturnIsoTooNew;
        }
        frameDiff = frameNumberStart - curFrameNumber;
        diff32 = (UInt32)frameDiff;
        if (diff32 < 2)
        {
            USBLog(5,"AppleUSBUHCI[%p]::CreateIsochTransfer WARNING! - frameNumberStart less than 2 ms (is %ld)!  frameNumberStart: %Ld, curFrameNumber: %Ld", this, diff32, frameNumberStart, curFrameNumber);
        }
    }
	
    pLLFrames = (IOUSBLowLatencyIsocFrame *)pFrames;
    //
    //  Get the total size of buffer
    //
    bufferSize = 0;
    for ( i = 0; i < frameCount; i++)
    {
        if (!lowLatency)
        {
            if (pFrames[i].frReqCount > pEP->maxPacketSize)
            {
                USBLog(1,"AppleUSBUHCI[%p]::CreateIsochTransfer - Isoch frame (%ld) too big (%d) MPS (%ld)", this, i + 1, pFrames[i].frReqCount, pEP->maxPacketSize);
                return kIOReturnBadArgument;
            }
            bufferSize += pFrames[i].frReqCount;
        } else
        {
            if (pLLFrames[i].frReqCount > pEP->maxPacketSize)
            {
                USBLog(1,"AppleUSBUHCI[%p]::CreateIsochTransfer(LL) - Isoch frame (%ld) too big (%d) MPS (%ld)", this, i + 1, pLLFrames[i].frReqCount, pEP->maxPacketSize);
                return kIOReturnBadArgument;
            }
            bufferSize += pLLFrames[i].frReqCount;
            
			// Make sure our frStatus field has a known value.  This is used by the client to know whether the transfer has been completed or not
            //
            pLLFrames[i].frStatus = requestFromRosettaClient ? (IOReturn) OSSwapInt32(kUSBLowLatencyIsochTransferKey) : (IOReturn) kUSBLowLatencyIsochTransferKey;
        }
    }
	
    // Format all the TDs, attach them to the pseudo endpoint.
    // let the frame interrupt routine put them in the periodic list
	
    transferOffset = 0;
	
    // Do this one frame at a time
    for ( i = 0; i < frameCount; i++)
    {
		UInt16			reqCount;
		UInt32			token, ctrlStatus;
		
        pNewITD = AllocateITD();
		if (lowLatency)
			reqCount = pLLFrames[i].frReqCount;
		else
			reqCount = pFrames[i].frReqCount;
	    
        USBLog(7, "AppleUSBUHCI[%p]::CreateIsochTransfer - new iTD %p size (%d)", this, pNewITD, reqCount);
        if (!pNewITD)
        {
            USBLog(1,"AppleUSBUHCI[%p]::CreateIsochTransfer Could not allocate a new iTD", this);
            return kIOReturnNoMemory;
        }
		
		pEP->firstAvailableFrame++;
        pNewITD->lowLatency = lowLatency;
		
		// set up the physical page pointers
		
		dmaStartAddr = pBuffer->getPhysicalSegment(transferOffset, &segLen);
		
		// TODO - use an alignment buffer if necessary for this frame
		if (segLen > reqCount)
		{
			segLen = reqCount;
		}
				
		if (segLen < reqCount)
		{
			UHCIAlignmentBuffer		*bp;
			
			// Use alignment buffer
			bp = pUEP->GetAlignmentBuffer();
			USBLog(6, "AppleUSBUHCI[%p]:CreateIsochTransfer - using alignment buffer %p at pPhysical %p instead of %p direction %s EP %d", this, (void*)bp->vaddr, (void*)bp->paddr, (void*)dmaStartAddr, (pEP->direction == kUSBOut) ? "OUT" : "IN", pEP->endpointNumber);
			pNewITD->buffer = bp;
			dmaStartAddr = bp->paddr;
			if (pEP->direction == kUSBOut) 
			{
				pBuffer->readBytes(transferOffset, (void *)bp->vaddr, reqCount);
			}
			bp->userBuffer = pBuffer;
			bp->userOffset = transferOffset;
			bp->userAddr = NULL;
		}

		transferOffset += reqCount;
		bufferSize -= reqCount;

        pNewITD->_pFrames = pFrames;
        pNewITD->_frameNumber = frameNumberStart + i;
		pNewITD->_frameIndex = i;
                
        // calculate IOC and completion if necessary
		if (i == (frameCount-1))
		{
			ioc = kUHCI_TD_IOC;
			pNewITD->_completion = completion;
		}
		else if (lowLatency)
		{
			if (!updateFrequency)
				ioc = (((i+1) % 8) == 0) ? (UInt32)kUHCI_TD_IOC : 0;
			else
				ioc = (((i+1) % updateFrequency) == 0) ? (UInt32)kUHCI_TD_IOC : 0;
		}
		else
			ioc = 0;
		
		pNewITD->GetSharedLogical()->buffer = HostToUSBLong(dmaStartAddr);
        pNewITD->SetPhysicalLink(kUHCI_QH_T);
		pNewITD->_lowLatency = lowLatency;
		
		token = myDirection | UHCI_TD_SET_MAXLEN(reqCount) | UHCI_TD_SET_ENDPT(pEP->endpointNumber) | UHCI_TD_SET_ADDR(pEP->functionAddress);
		pNewITD->GetSharedLogical()->token = HostToUSBLong(token);
		
		ctrlStatus = kUHCI_TD_ACTIVE | kUHCI_TD_ISO | UHCI_TD_SET_ACTLEN(0) | ioc;
		pNewITD->GetSharedLogical()->ctrlStatus = HostToUSBLong(ctrlStatus);
		
		pNewITD->_pEndpoint = pEP;
		
		pNewITD->_requestFromRosettaClient = requestFromRosettaClient ? true : false;
		
		PutTDonToDoList(pEP, pNewITD);
    }
	USBLog(7, "AppleUSBUHCI[%p]::CreateIsochTransfer - calling AddIsochFramesToSchedule", this);
	AddIsochFramesToSchedule(pEP);
	return kIOReturnSuccess;
}


IOReturn
AppleUSBUHCI::UIMCreateIsochTransfer(short					functionNumber,
                                     short					endpointNumber,
                                     IOUSBIsocCompletion	completion,
                                     UInt8					direction,
                                     UInt64					frameStart,
                                     IOMemoryDescriptor		*pBuffer,
                                     UInt32					frameCount,
                                     IOUSBIsocFrame			*pFrames)
{
    IOUSBControllerIsochEndpoint*		pEP;
	bool								requestFromRosettaClient = false;
	
    USBLog(7, "AppleUSBUHCI[%p]::UIMCreateIsochTransfer - adr=%d:%d cbp=%p:%lx (cback=[%lx:%lx:%lx])", this,  
		   functionNumber, endpointNumber, pBuffer, 
		   pBuffer->getLength(), 
		   (UInt32)completion.action, (UInt32)completion.target, 
		   (UInt32)completion.parameter);
	
    if ( (frameCount == 0) || (frameCount > 1000) )
    {
        USBLog(3,"AppleUSBUHCI[%p]::UIMCreateIsochTransfer bad frameCount: %ld", this, frameCount);
        return kIOReturnBadArgument;
    }
	
	// Determine if our request came from a rosetta client
	if ( direction & 0x80 )
	{
		requestFromRosettaClient = true;
		direction &= ~0x80;
	}
	
    pEP = FindIsochronousEndpoint(functionNumber, endpointNumber, direction, NULL);
    if(pEP == NULL)
    {
        USBLog(1, "AppleUSBUHCI[%p]::UIMCreateIsochTransfer - Endpoint not found", this);
        return kIOUSBEndpointNotFound;        
    }
    
	return CreateIsochTransfer(pEP, completion, frameStart, pBuffer, frameCount, pFrames, 0, false, requestFromRosettaClient);
}



IOReturn 
AppleUSBUHCI::UIMCreateIsochTransfer(short						functionNumber,
                                     short						endpointNumber,
                                     IOUSBIsocCompletion		completion,
                                     UInt8						direction,
                                     UInt64						frameStart,
                                     IOMemoryDescriptor			*pBuffer,
                                     UInt32						frameCount,
                                     IOUSBLowLatencyIsocFrame	*pFrames,
                                     UInt32						updateFrequency)
{
    IOUSBControllerIsochEndpoint*		pEP;
	bool								requestFromRosettaClient = false;
	
    USBLog(7, "AppleUSBUHCI[%p]::UIMCreateIsochTransfer - LL - adr=%d:%d cbp=%p:%lx (cback=[%lx:%lx:%lx])", this, functionNumber, endpointNumber, pBuffer, pBuffer->getLength(), (UInt32)completion.action, (UInt32)completion.target, (UInt32)completion.parameter);

    if ( (frameCount == 0) || (frameCount > 1000) )
    {
        USBLog(3,"AppleUSBUHCI[%p]::UIMCreateIsochTransfer bad frameCount: %ld", this, frameCount);
        return kIOReturnBadArgument;
    }
	
	// Determine if our request came from a rosetta client
	if ( direction & 0x80 )
	{
		requestFromRosettaClient = true;
		direction &= ~0x80;
	}
	
    pEP = FindIsochronousEndpoint(functionNumber, endpointNumber, direction, NULL);
	
    if(pEP == NULL)
    {
        USBLog(1, "AppleUSBUHCI[%p]::UIMCreateIsochTransfer - Endpoint not found", this);
        return kIOUSBEndpointNotFound;        
    }
    
	return CreateIsochTransfer(pEP, completion, frameStart, pBuffer, frameCount, (IOUSBIsocFrame*)pFrames, updateFrequency, true, requestFromRosettaClient);
}


// ========================================================================
#pragma mark Endpoints
// ========================================================================


IOReturn 
AppleUSBUHCI::HandleEndpointAbort(short functionAddress, short endpointNumber, short direction, bool clearToggle)
{
    AppleUHCIQueueHead						*pQH, *pQHPrev;
	UInt32									link;
	IOUSBControllerIsochEndpoint			*pIsochEP;
	AppleUHCITransferDescriptor				*savedFirstTD = NULL;
	AppleUHCITransferDescriptor				*savedLastTD = NULL;
    
    USBLog(4, "AppleUSBUHCI[%p]::HandleEndpointAbort: Addr: %d, Endpoint: %d,%d", this, functionAddress, endpointNumber, direction);
	
    if (functionAddress == _rootFunctionNumber)
    {
        if ( (endpointNumber != 1) && (endpointNumber != 0) )
        {
            USBLog(1, "AppleUSBUHCI[%p]::HandleEndpointAbort: bad params - endpNumber: %d", this, endpointNumber );
            return kIOReturnBadArgument;
        }
        
        // We call SimulateEDDelete (endpointNumber, direction) in 9
        //
        USBLog(5, "AppleUSBUHCI[%p]::HandleEndpointAbort: Attempting operation on root hub", this);
        return RHAbortEndpoint(endpointNumber, direction);
    }
	
    pIsochEP = FindIsochronousEndpoint(functionAddress, endpointNumber, direction, NULL);
    if (pIsochEP)
    {
		return AbortIsochEP(pIsochEP);
    }
    
	if (pQH = FindQueueHead(functionAddress, endpointNumber, direction, kUSBControl, &pQHPrev), pQH)
	{
		USBLog(5, "AppleUSBUHCI[%p]::HandleEndpointAbort - Found control queue head %p prev %p", this, pQH, pQHPrev);
	}
	else if (pQH = FindQueueHead(functionAddress, endpointNumber, direction, kUSBInterrupt, &pQHPrev), pQH)
	{
		USBLog(5, "AppleUSBUHCI[%p]::HandleEndpointAbort - Found interrupt queue head %p prev %p", this, pQH, pQHPrev);
	}
	else if (pQH = FindQueueHead(functionAddress, endpointNumber, direction, kUSBBulk, &pQHPrev), pQH)
	{
		USBLog(5, "AppleUSBUHCI[%p]::HandleEndpointAbort - Found bulk queue head %p prev %p", this, pQH, pQHPrev);
	}
	
    if (pQH == NULL) 
	{
		USBLog(1, "AppleUSBUHCI[%p]::HandleEndpointAbort - endpoint not found", this);
        return kIOUSBEndpointNotFound;
    }
	
	// we don't need to handle any TDs if the queue is empty, so check for that..
    if(pQH->firstTD != pQH->lastTD)			// There are transactions on this queue
    {
		// we need to unlink the QH, since we can't modify the elink field if it is active
		// don't call UnlinkQueueHead, since we don't want to change the markers
		link = pQHPrev->GetPhysicalLink();						// save the link to ourself
		pQHPrev->SetPhysicalLink(pQH->GetPhysicalLink());		// temporarily change the physical link
		IOSleep(1);												// make sure the HW is not looking at it
        USBLog(4, "AppleUSBUHCI[%p]::HandleEndpointAbort: removing TDs", this);
		savedFirstTD = pQH->firstTD;
		savedLastTD = pQH->lastTD;
        pQH->firstTD = pQH->lastTD;
		if (clearToggle)
			pQH->firstTD->GetSharedLogical()->token = 0;		// this will reset the data toggle
		pQH->GetSharedLogical()->elink = HostToUSBLong(pQH->firstTD->GetPhysicalAddrWithType());
		IOSync();
		pQHPrev->SetPhysicalLink(link);							// restore it to the original value
    }
	else
	{
		if (clearToggle)
			pQH->firstTD->GetSharedLogical()->token = 0;	// this will reset the data toggle

		pQH->GetSharedLogical()->elink = HostToUSBLong(pQH->firstTD->GetPhysicalAddrWithType());
		IOSync();
	}
	pQH->stalled = false;

    USBLog(4, "AppleUSBUHCI[%p]::HandleEndpointAbort: Addr: %d, Endpoint: %d,%d - calling DoDoneQueue", this, functionAddress, endpointNumber, direction);
	UHCIUIMDoDoneQueueProcessing(savedFirstTD, kIOUSBTransactionReturned, savedLastTD);
	
    return kIOReturnSuccess;
	
}



IOReturn 
AppleUSBUHCI::UIMAbortEndpoint(short functionAddress, short endpointNumber, short direction)
{
    USBLog(3, "AppleUSBUHCI[%p]::UIMAbortEndpoint - endpoint %d:%d,%d", this, functionAddress, endpointNumber, direction);
    return HandleEndpointAbort(functionAddress, endpointNumber, direction, false);
}



IOReturn 
AppleUSBUHCI::UIMClearEndpointStall(short functionAddress, short endpointNumber, short direction)
{
    
    USBLog(5, "AppleUSBUHCI[%p]::UIMClearEndpointStall - endpoint %d:%d,%d", this, functionAddress, endpointNumber, direction);
    return  HandleEndpointAbort(functionAddress, endpointNumber, direction, true);
}



IOReturn
AppleUSBUHCI::UIMDeleteEndpoint(short functionNumber, short	endpointNumber, short direction)
{
    AppleUHCIQueueHead					*pQH;
	IOUSBControllerIsochEndpoint*		pIsochEP;
    int									i;
	AppleUHCIQueueHead					*pQHPrev = NULL;
	IOReturn							err;

    USBLog(3, "AppleUSBUHCI[%p]::UIMDeleteEndpoint %d %d %d", this, functionNumber, endpointNumber, direction);
        
    if (functionNumber == _rootFunctionNumber) 
	{
        if (endpointNumber != 0 && endpointNumber != 1) 
		{
			USBLog(1, "AppleUSBUHCI[%p]::UIMDeleteEndpoint - not ep 0 or ep 1 [%d](kIOReturnBadArgument)", this, endpointNumber);
            return kIOReturnBadArgument;
        }
        return RHDeleteEndpoint(endpointNumber, direction);
    }

    pIsochEP = FindIsochronousEndpoint(functionNumber, endpointNumber, direction, NULL);
    if (pIsochEP)
		return DeleteIsochEP(pIsochEP);
	
	if (pQH = FindQueueHead(functionNumber, endpointNumber, direction, kUSBControl, &pQHPrev), pQH)
	{
		USBLog(7, "AppleUSBUHCI[%p]::UIMDeleteEndpoint - Found control queue head %p prev %p", this, pQH, pQHPrev);
	}
	else if (pQH = FindQueueHead(functionNumber, endpointNumber, direction, kUSBInterrupt, &pQHPrev), pQH)
	{
		USBLog(7, "AppleUSBUHCI[%p]::UIMDeleteEndpoint - Found interrupt queue head %p prev %p", this, pQH, pQHPrev);
	}
	else if (pQH = FindQueueHead(functionNumber, endpointNumber, direction, kUSBBulk, &pQHPrev), pQH)
	{
		USBLog(7, "AppleUSBUHCI[%p]::UIMDeleteEndpoint - Found bulk queue head %p prev %p", this, pQH, pQHPrev);
	}
	
    if (pQH == NULL) 
	{
		USBLog(1, "AppleUSBUHCI[%p]::UIMDeleteEndpoint - endpoint not found", this);
        return kIOUSBEndpointNotFound;
    }
			
	err = UnlinkQueueHead(pQH, pQHPrev);
	if (err)
	{
		USBLog(1, "AppleUSBUHCI[%p]::UIMDeleteEndpoint - err %p unlinking endpoint", this, (void*)err);
	}
	
    if(pQH->firstTD != pQH->lastTD)			// There are transactions on this queue
    {
        USBLog(5, "AppleUSBUHCI[%p]::UIMDeleteEndpoint: removing TDs", this);
        UHCIUIMDoDoneQueueProcessing(pQH->firstTD, kIOUSBTransactionReturned, pQH->lastTD);
        pQH->firstTD = pQH->lastTD;
    }
	
	if ( pQH->firstTD != NULL )
	{
		// I need to delete the dummy TD
		USBLog(7, "AppleUSBUHCI[%p]::UIMDeleteEndpoint - deallocating the dummy TD", this);
		DeallocateTD(pQH->firstTD);
		pQH->firstTD = NULL;
    }
	
    USBLog(7, "AppleUSBUHCI[%p]::UIMDeleteEndpoint: Deallocating %p", this, pQH);
    DeallocateQH(pQH);
	
    return kIOReturnSuccess;
}



AppleUHCIQueueHead *
AppleUSBUHCI::FindQueueHead(short functionNumber, short endpointNumber, UInt8 direction, UInt8 type, AppleUHCIQueueHead **ppQHPrev)
{
    AppleUHCIQueueHead		*pQH, *firstQH, *lastQH, *prevQH = NULL;
	
    USBLog(7, "AppleUSBUHCI[%p]::FindQueueHead(%d, %d, %d, %d)", this, functionNumber, endpointNumber, direction, type);
	switch (type)
	{
		case kUSBInterrupt:
			firstQH = _intrQH[kUHCI_NINTR_QHS-1];
			lastQH = _lsControlQHStart;
			break;
			
		case kUSBControl:
			firstQH = _lsControlQHStart;
			lastQH = _bulkQHStart;
			break;
			
		case kUSBBulk:
			firstQH = _bulkQHStart;
			lastQH = _lastQH;
			break;
				
		default:
			firstQH = lastQH  = NULL;
	}

    pQH = firstQH;
	while (pQH && (pQH != lastQH))
	{
		USBLog(7, "AppleUSBUHCI[%p]::FindQueueHead - looking at queue head %p (%d, %d, %d, %d)", this, pQH, pQH->functionNumber, pQH->endpointNumber, pQH->direction, pQH->type);
		if ((pQH->functionNumber == functionNumber) && (pQH->endpointNumber == endpointNumber) && ((direction == kUSBAnyDirn) || (pQH->direction == direction)) && (pQH->type == type))
		{
			USBLog(7, "AppleUSBUHCI[%p]::FindQueueHead - found Queue Head %p", this, pQH);
			if (ppQHPrev)
				*ppQHPrev = prevQH;
			return pQH;
		}
		prevQH = pQH;
		pQH = OSDynamicCast(AppleUHCIQueueHead, pQH->_logicalNext);
	}
    USBLog(7, "AppleUSBUHCI[%p]::FindQueueHead - endpoint not found", this);
    return NULL;
}



IOReturn
AppleUSBUHCI::UnlinkQueueHead(AppleUHCIQueueHead *pQH, AppleUHCIQueueHead *pQHBack)
{
	if (!pQH || !pQHBack)
	{
		USBLog(1, "AppleUSBUHCI[%p]::UnlinkQueueHead - invalid params (%p, %p)", this, pQH, pQHBack);
		return kIOReturnBadArgument;
	}
	USBLog(7, "AppleUSBUHCI[%p]::UnlinkQueueHead(%p, %p)", this, pQH, pQHBack);
	// need to back out the end markers if appropriate
	if (pQH == _lsControlQHEnd)
		_lsControlQHEnd = pQHBack;
	else if (pQH == _fsControlQHEnd)
		_fsControlQHEnd = pQHBack;
	else if (pQH == _bulkQHEnd)
		_bulkQHEnd = pQHBack;
		
	// change the hardware and software link pointers
	pQHBack->SetPhysicalLink(pQH->GetPhysicalLink());
	pQHBack->_logicalNext = pQH->_logicalNext;
	
	// there is no doorbell in UHCI like in EHCI
	// we need to make sure that the queue head is not cached in the controller
	// the easiest way to do so it to just wait 1 ms for the next frame
	IOSleep(1);
	
	return kIOReturnSuccess;
}



// ========================================================================
#pragma mark Transaction starting and completing
// ========================================================================


#define	kUHCIUIMScratchFirstActiveFrame	0

void 
AppleUSBUHCI::UIMCheckForTimeouts(void)
{
    AbsoluteTime					currentTime, t;
    UInt64							elapsedTime;
    UInt64							frameNumber;
    UInt16							status;
	AppleUHCIQueueHead				*pQH = NULL, *pQHBack = NULL, *pQHBack1 = NULL;
	AppleUHCITransferDescriptor		*pTD = NULL;
	IOPhysicalAddress				pTDPhys;
    UInt32							noDataTimeout;
    UInt32							completionTimeout;
    UInt32							curFrame = GetFrameNumber32();
	UInt32							rem;
	bool							logging = false;
	int								loopCount = 0;
	

	if (_needToCreateRootHub)
	{
		USBLog(1,"AppleUSBUHCI[%p]::UIMCheckForTimeouts - Need to recreate root hub on bus %ld INTR[%p] _uhciBusState[%d] _uhciAvailable[%s], sleeping for 5 seconds", this, _busNumber, (void*)ioRead16(kUHCI_INTR), _uhciBusState, _uhciAvailable ? "true" : "false");
		_needToCreateRootHub = false;
		
		IOSleep(5000);  // Sleep for 5s
		
		USBLog(2,"AppleUSBUHCI[%p]::UIMCheckForTimeouts -  Need to recreate root hub on bus %ld, powering up hardware", this, _busNumber);
		
		// Initialize our hardware
		//
		UIMInitializeForPowerUp();
		_uhciAvailable = true;										// tell the interrupt filter routine that we are on
		Run(true);
		_uhciBusState = kUHCIBusStateRunning;
		_wakingFromHibernation = false;
		
		if ( _rootHubDevice == NULL )
		{
			IOReturn err;
			
			err = CreateRootHubDevice( _device, &_rootHubDevice );
			if ( err != kIOReturnSuccess )
			{
				USBError(1,"AppleUSBUHCI[%p]::UIMCheckForTimeouts -  Could not create root hub device upon wakeup (%x)!", this, err);
			}
			else
			{
				USBLog(2,"AppleUSBUHCI[%p]::UIMCheckForTimeouts -  calling registerService on new root hub", this);
				_rootHubDevice->registerService(kIOServiceRequired | kIOServiceSynchronous);
			}
		}
		USBLog(1,"AppleUSBUHCI[%p]::UIMCheckForTimeouts - done creating root hub on bus %ld INTR[%p] _uhciBusState[%d] _uhciAvailable[%s]", this, _busNumber, (void*)ioRead16(kUHCI_INTR), _uhciBusState, _uhciAvailable ? "true" : "false");
		return;				// nothing else to do if we are just creating the root hub
	}
	
    if (isInactive() || (_uhciBusState != kUHCIBusStateRunning) || _wakingFromHibernation)
	{
        return;
    }
	
    // Check to see if we missed an interrupt.
	USBLog(7, "AppleUSBUHCI[%p]::UIMCheckForTimeouts - calling ProcessCompletedTransactions", this);
	ProcessCompletedTransactions();
	
    clock_get_uptime(&currentTime);
    
    status = ioRead16(kUHCI_STS);
    if (status & kUHCI_STS_HCH) 
	{
        // acknowledge
        ioWrite16(kUHCI_STS, kUHCI_STS_HCH);
        
        USBError(1, "AppleUSBUHCI[%p]::UIMCheckForTimeouts - Host controller halted, resetting", this);
        Reset(true);
        Run(true);
    }
    
    // Adjust 64-bit frame number.
	// This is a side-effect of GetFrameNumber().
    frameNumber = GetFrameNumber();
    
    
    _lastTimeoutFrameNumber = frameNumber;
    _lastFrameNumberTime = currentTime;

	for (pQH = _lsControlQHStart; pQH && (loopCount++ < 100); pQH = OSDynamicCast(AppleUHCIQueueHead, pQH->_logicalNext))
	{
		if (pQH == pQH->_logicalNext)
		{
			USBError(1,"AppleUSBUHCI[%p]::UIMCheckForTimeouts  pQH (%p) linked to itself", this, pQH);
		}
		
		// Need to keep a note of the previous ED for back links. Usually I'd
		// put a pEDBack = pED at the end of the loop, but there are lots of 
		// continues in this loop so it was getting skipped (and unlinking the
		// entire async schedule). These lines get the ED from the previous 
		// interation in pEDBack.
		pQHBack = pQHBack1;
		pQHBack1 = pQH;
		
		if (pQH->type == kQHTypeDummy)
			continue;
		
		logging = true;
		USBLog(7, "AppleUSBUHCI[%p]::UIMCheckForTimeouts - checking QH [%p]", this, pQH);
		pQH->print(7);

		// OHCI gets phys pointer and logicals that, that seems a little complicated, so
		// I'll get the logical pointer and compare it to the phys. If they're different,
		// this transaction has only just got to the head and the previous one(s) haven't
		// been scavenged yet. Assume its not a good candidate for a timeout.
		
		// get the top TD
		pTDPhys = USBToHostLong(pQH->GetSharedLogical()->elink);
		pTD = pQH->firstTD;
		
		if (!pTD)
		{
			USBLog(3, "AppleUSBUHCI[%p]::UIMCheckForTimeouts - no TD", this);
			continue;
		}
		
		if (!pTD->command)
		{
			USBLog(7, "AppleUSBUHCI[%p]::UIMCheckForTimeouts - found a TD without a command - moving on", this);
			continue;
		}
		
		if (pTD == pQH->lastTD)
		{
			USBLog(1, "AppleUSBUHCI[%p]::UIMCheckForTimeouts - ED (%p) - TD is TAIL but there is a command - pTD (%p)", this, pQH, pTD);
			pQH->print(1);
		}
		
		if(pTDPhys != pTD->GetPhysicalAddrWithType())
		{
			USBLog(5, "AppleUSBUHCI[%p]::UIMCheckForTimeouts - pED (%p) - mismatched logical and physical - TD (%p) will be scavenged later", this, pQH, pTD);
			pQH->print(5);
			pTD->print(5);
			continue;
		}
		
		noDataTimeout = pTD->command->GetNoDataTimeout();
		completionTimeout = pTD->command->GetCompletionTimeout();
		
		if (completionTimeout)
		{
			UInt32	firstActiveFrame = pTD->command->GetUIMScratch(kUHCIUIMScratchFirstActiveFrame);
			if (!firstActiveFrame)
			{
				pTD->command->SetUIMScratch(kUHCIUIMScratchFirstActiveFrame, curFrame);
				continue;
			}
			if ((curFrame - firstActiveFrame) >= completionTimeout)
			{
				USBLog(2, "AppleUSBUHCI[%p]::UIMCheckForTimeouts - Found a TD [%p] on QH [%p] past the completion deadline, timing out! (0x%lx - 0x%lx)", this, pTD, pQH, curFrame, firstActiveFrame);
				USBError(1, "AppleUSBUHCI[%p]::Found a transaction past the completion deadline on bus %ld, timing out!", this, _busNumber);
				pQH->print(2);
				ReturnOneTransaction(pTD, pQH, pQHBack, kIOUSBTransactionTimeout);
				continue;
			}
		}
		
		if (!noDataTimeout)
			continue;
		
		if (!pTD->lastFrame || (pTD->lastFrame > curFrame))
		{
			// this pTD is not a candidate yet, remember the frame number and go on
			pTD->lastFrame = curFrame;
			pTD->lastRemaining = findBufferRemaining(pQH);
			continue;
		}
		rem = findBufferRemaining(pQH);
		
		if (pTD->lastRemaining != rem)
		{
			// there has been some activity on this TD. update and move on
			pTD->lastRemaining = rem;
			continue;
		}
		if ((curFrame - pTD->lastFrame) >= noDataTimeout)
		{
			USBLog(2, "AppleUSBUHCI[%p]UIMCheckForTimeouts:  Found a transaction (%p) which hasn't moved in 5 seconds, timing out! (0x%lx - 0x%lx)(CMD:%p STS:%p INTR:%p PORTSC1:%p PORTSC2:%p FRBASEADDR:%p ConfigCMD:%p)", this, pTD, curFrame, pTD->lastFrame, (void*)ioRead16(kUHCI_CMD), (void*)ioRead16(kUHCI_STS), (void*)ioRead16(kUHCI_INTR), (void*)ioRead16(kUHCI_PORTSC1), (void*)ioRead16(kUHCI_PORTSC2), (void*)ioRead32(kUHCI_FRBASEADDR), (void*)_device->configRead16(kIOPCIConfigCommand));
			//PrintFrameList(curFrame & kUHCI_NVFRAMES_MASK, 7);
			USBError(1, "AppleUSBUHCI[%p]::Found a transaction which hasn't moved in 5 seconds on bus %ld, timing out!", this, _busNumber);
			pQH->print(2);
			pTD->print(2);
			ReturnOneTransaction(pTD, pQH, pQHBack, kIOUSBTransactionTimeout);
			//printED(pED);
			//printTD(pTD);
			//printAsyncQueue();
			
			continue;
		}
	}

	t = currentTime;
    SUB_ABSOLUTETIME(&t, &_lastTime);
    absolutetime_to_nanoseconds(t, &elapsedTime);
	elapsedTime /= 1000000000;				// Convert to seconds from nanoseconds
    
    //USBLog(5, "AppleUSBUHCI[%p]::UIMCheckForTimeouts - elapsed rh time: %d", this, elapsedTime);
    
    if (elapsedTime > kUHCICheckForRootHubConnectionsPeriod) 
	{
        
        _lastTime = currentTime;
        
        if (_uhciBusState != kUHCIBusStateSuspended) 
		{
            
            USBLog(7, "AppleUSBUHCI[%p]::UIMCheckForTimeouts - checking root hub for connections", this);
            
            // Check to see if the root hub has no connections
            if (RHAreAllPortsDisconnectedOrSuspended()) 
			{
                t = currentTime;
                SUB_ABSOLUTETIME(&t, &_rhChangeTime);
                absolutetime_to_nanoseconds(t, &elapsedTime);
				elapsedTime /= 1000000000;				// Convert to seconds from nanoseconds
                
                if (elapsedTime >= kUHCICheckForRootHubInactivityPeriod) 
				{
                    USBLog(2,"AppleUSBUHCI[%p]::UIMCheckForTimeouts - Suspending idle root hub", this);
                    setPowerState( kUHCIPowerLevelIdleSuspend, this);
                }
                // XXX should we set suspend change status bit here?
            }
        }
    }
	
	if (loopCount > 99)
	{
		USBError(1,"AppleUSBUHCI[%p]::UIMCheckForTimeouts  Too many loops around", this);
	}
	
	if (logging)
		USBLog(7, "AppleUSBUHCI[%p]::UIMCheckForTimeouts - done", this);
}



void 
AppleUSBUHCI::ReturnOneTransaction(AppleUHCITransferDescriptor		*pTD,
								   AppleUHCIQueueHead				*pQH,
								   AppleUHCIQueueHead				*pQHBack,
								   IOReturn							err)
{
	AppleUHCITransferDescriptor		*pTDFirst = pTD;
	
	// momentarily take the queue head out of the queue list so we can modify the transaction
	pQHBack->SetPhysicalLink(pQH->GetPhysicalLink());
	IOSleep(1);								// have to make sure the controller doesn't have it in the cache
		
    while(pTD != NULL)
    {
		if (pTD->lastTDofTransaction)
			break;

		pTD = OSDynamicCast(AppleUHCITransferDescriptor, pTD->_logicalNext);
    }
    if(pTD == NULL)
    {
		// This works, sort of, NULL for an end transction means remove them all.
		// But there will be no callback
		USBLog(1, "AppleUSBUHCI[%p]::ReturnOneTransaction - got to the end with no callback", this);
    }
    else
    {
		pTD = OSDynamicCast(AppleUHCITransferDescriptor, pTD->_logicalNext);
		pQH->GetSharedLogical()->elink = HostToUSBLong(pTD->GetPhysicalAddrWithType());
		IOSync();
		pQH->firstTD = pTD;
		pQHBack->SetPhysicalLink(pQH->GetPhysicalAddrWithType());						// link us back in
    }
	USBLog(5, "AppleUSBUHCI[%p]::ReturnOneTransaction - returning transactions", this);
    UHCIUIMDoDoneQueueProcessing(pTDFirst, err, pTD);
}



UInt32 
AppleUSBUHCI::findBufferRemaining(AppleUHCIQueueHead *pQH)
{
	UInt32							ctrlStatus, token, bufferSizeRemaining = 0;
	AppleUHCITransferDescriptor		*pTD = pQH->firstTD;
	
	while (pTD && (pTD != pQH->lastTD))
	{
		ctrlStatus = USBToHostLong(pTD->GetSharedLogical()->ctrlStatus);
		token = USBToHostLong(pTD->GetSharedLogical()->token);
		bufferSizeRemaining += (UHCI_TD_GET_MAXLEN(token) - UHCI_TD_GET_ACTLEN(ctrlStatus));
		if (pTD->lastTDofTransaction)
			break;
		pTD = OSDynamicCast(AppleUHCITransferDescriptor, pTD->_logicalNext);
	}
	
	return bufferSizeRemaining;
}



IOReturn
AppleUSBUHCI::TDToUSBError(UInt32 status)
{
    IOReturn result;
    
    status &= kUHCI_TD_ERROR_MASK;
    if (status == 0) 
	{
        result = kIOReturnSuccess;
    } else if (status & kUHCI_TD_CRCTO) 
	{
        // In this case, the STALLED bit is also set
        result = kIOReturnNotResponding;
    } else if (status & kUHCI_TD_BABBLE) 
	{
        // In this case, the STALLED bit is probably also set
        result = kIOReturnOverrun;
    } else if (status & kUHCI_TD_STALLED) 
	{
        result = kIOUSBPipeStalled;
    } else if (status & kUHCI_TD_DBUF)
	{
        result = kIOReturnOverrun;
    } else if (status & kUHCI_TD_CRCTO) 
	{
        result = kIOUSBCRCErr;
    } else if (status & kUHCI_TD_BITSTUFF) 
	{
        result = kIOUSBBitstufErr;
    } else 
	{
        result = kIOUSBTransactionReturned;
    }
    return result;
}



// ========================================================================
#pragma mark Transaction descriptors
// ========================================================================


IOReturn
AppleUSBUHCI::AllocTDChain(AppleUHCIQueueHead* pQH, IOUSBCommand *command, IOMemoryDescriptor* CBP, UInt32 bufferSize, UInt16 direction, Boolean controlTransaction)
{
	
    AppleUHCITransferDescriptor			*pTD1, *pTD, *pTDnew, *pTDLast;
    UInt32								myToggle = 0;
    UInt32								myDirection = 0;
    IOByteCount							transferOffset;
    UInt32								token;
	UInt32								ctrlStatus;
    IOReturn							status = kIOReturnSuccess;
    UInt32								maxPacket;
    UInt32								totalPhysLength;
    IOPhysicalAddress					dmaStartAddr;
    UInt32								bytesToSchedule;
	
	/* *********** Note: Always put the flags in the TD last. ************** */
	/* *********** This is what kicks off the transaction if  ************** */
	/* *********** the next and alt pointers are not set up   ************** */
	/* *********** then the controller will pick up and cache ************** */
	/* *********** crap for the TD.                           ************** */
	
    if(controlTransaction)
    {
		if(direction != kUSBNone)
		{												// Setup phase uses Data 0, data phase & status phase use Data1 
            myToggle = kUHCI_TD_D;						// use Data1 
		}
    }
	else
	{
		myToggle = USBToHostLong(pQH->lastTD->GetSharedLogical()->token) & kUHCI_TD_D;
	}
	
	switch (direction)
	{
		case kUSBIn:
			myDirection = kUHCI_TD_PID_IN;
			break;
			
		case kUSBOut:
			myDirection = kUHCI_TD_PID_OUT;
			break;
			
		default:
			myDirection = kUHCI_TD_PID_SETUP;
	}
    maxPacket   =  pQH->maxPacketSize;
	if ((bufferSize > 0) && (maxPacket == 0))
	{
		USBError(1, "AppleUSBUHCI[%p]::AllocTDChain - buffserSize %d with maxPacket %d", this, (int)bufferSize, (int)maxPacket);
		return kIOReturnNoResources;
	}
	
    // First allocate the first of the new bunch
    pTD1 = AllocateTD(pQH);
	
    if (pTD1 == NULL)
    {
		USBError(1, "AppleUSBUHCI[%p]::AllocTDChain - can't allocate 1st new TD", this);
		return kIOReturnNoMemory;
    }
    pTD = pTD1;	// We'll be working with pTD
	
	ctrlStatus = kUHCI_TD_ACTIVE | UHCI_TD_SET_ERRCNT(3) | UHCI_TD_SET_ACTLEN(0);

	// if an IN token, allow short packet detect
	if (direction == kUSBIn)
		ctrlStatus |= kUHCI_TD_SPD;
	
	// If device is low speed, set LS bit in status.
	if (pQH->speed == kUSBDeviceSpeedLow) 
		ctrlStatus |= kUHCI_TD_LS;
	
    if (bufferSize != 0)
    {	    
        transferOffset = 0;
        while (transferOffset < bufferSize)
        {
			dmaStartAddr = CBP->getPhysicalSegment(transferOffset, &totalPhysLength);
			USBLog(7, "AppleUSBUHCI[%p]::AllocTDChain - getPhysicalSegment returned length of %ld (out of %ld) and start of 0x%lx", this, totalPhysLength, bufferSize, dmaStartAddr);
			bytesToSchedule = 0;
			if ((bufferSize-transferOffset) > maxPacket)
			{
				bytesToSchedule = maxPacket;
			}
			else 
			{
				bytesToSchedule = (bufferSize-transferOffset);
			}
				
			if (totalPhysLength < bytesToSchedule)
			{
                UHCIAlignmentBuffer *bp;
                
                // Use alignment buffer
                bp = pQH->GetAlignmentBuffer();
                USBLog(1, "AppleUSBUHCI[%p]:AllocTDChain - using alignment buffer %p at pPhysical %p instead of %p", this, (void*)bp->vaddr, (void*)bp->paddr, (void*)dmaStartAddr);
                pTD->buffer = bp;
                dmaStartAddr = bp->paddr;
                if (direction != kUSBIn) 
				{
                    CBP->readBytes(transferOffset, (void *)bp->vaddr, bytesToSchedule);
                }
                bp->userBuffer = CBP;
                bp->userOffset = transferOffset;
                bp->userAddr = NULL;
			}
			
			transferOffset += bytesToSchedule;
			pTD->direction = direction;
			pTD->GetSharedLogical()->buffer = HostToUSBLong(dmaStartAddr);
						
			token = myDirection | myToggle | UHCI_TD_SET_MAXLEN(bytesToSchedule) | UHCI_TD_SET_ENDPT(pQH->endpointNumber) | UHCI_TD_SET_ADDR(pQH->functionNumber);
			pTD->GetSharedLogical()->token = HostToUSBLong(token);
			myToggle = myToggle ? 0 : (int)kUHCI_TD_D;
			
			USBLog(7, "AppleUSBUHCI[%p]::AllocTDChain - putting command into TD (%p) on QH (%p)", this, pTD, pQH);
			pTD->command = command;								// Do like OHCI, link to command from each TD
            if (transferOffset >= bufferSize)
            {
				ctrlStatus |= kUHCI_TD_IOC;
				pTD->lastTDofTransaction = true;
				pTD->logicalBuffer = CBP;
				pTD->GetSharedLogical()->ctrlStatus = HostToUSBLong(ctrlStatus & ~kUHCI_TD_SPD);		// make sure the SPD bit is clear on the last TD (rdar://4464945)
            }
			else
            {
				pTD->lastTDofTransaction = false;
				pTDnew = AllocateTD(pQH);
				if (pTDnew == NULL)
				{
					status = kIOReturnNoMemory;
					USBError(1, "AppleUSBUHCI[%p]::AllocTDChain can't allocate new TD", this);
				}
				else
				{
					pTD->SetPhysicalLink(pTDnew->GetPhysicalAddrWithType());
					pTD->_logicalNext = pTDnew;																	// if(trace)printTD(pTD);
					pTD->GetSharedLogical()->ctrlStatus = HostToUSBLong(ctrlStatus);
					pTD = pTDnew;
					USBLog(7, "AppleUSBUHCI[%p]::AllocTDChain - got another TD - going to fill it up too (%ld, %ld)", this, transferOffset, bufferSize);
				}
            }
        }
    }
    else
    {
		// no buffer to transfer
		USBLog(7, "AppleUSBUHCI[%p]::AllocTDChain - (no buffer)- putting command into TD (%p) on QH (%p)", this, pTD, pQH);
		pTD->command = command;
		ctrlStatus |= kUHCI_TD_IOC;
		pTD->lastTDofTransaction = true;
		pTD->logicalBuffer = CBP;
		token = myDirection | myToggle | UHCI_TD_SET_MAXLEN(0) | UHCI_TD_SET_ENDPT(pQH->endpointNumber) | UHCI_TD_SET_ADDR(pQH->functionNumber);
		pTD->GetSharedLogical()->token = HostToUSBLong(token);
		pTD->GetSharedLogical()->ctrlStatus = HostToUSBLong(ctrlStatus);
		myToggle = myToggle ? 0 : (int)kUHCI_TD_D;
	}
	
    pTDLast = pQH->lastTD;
	
	pTD->SetPhysicalLink(pTD1->GetPhysicalAddrWithType());
	pTD->_logicalNext = pTD1;
	
    // We now have a new chain of TDs. link it in.
    // pTD1, pointer to first TD
    // pTD, pointer to last TD
    // pTDLast is last currently on endpoint, will be made first
	
    ctrlStatus = pTD1->GetSharedLogical()->ctrlStatus;
	
    pTD1->GetSharedLogical()->ctrlStatus = 0;										// turn off the active bit before we make it live
	
    // Copy contents of first TD to old tail
    pTDLast->SetPhysicalLink(pTD1->GetPhysicalLink());
    pTDLast->GetSharedLogical()->ctrlStatus = pTD1->GetSharedLogical()->ctrlStatus;
	pTDLast->GetSharedLogical()->token = pTD1->GetSharedLogical()->token;
	pTDLast->GetSharedLogical()->buffer = pTD1->GetSharedLogical()->buffer;
    
    USBLog(7, "AppleUSBUHCI[%p]::AllocTDChain - transfering command from TD (%p) to TD (%p)", this, pTD1, pTDLast);
    pTDLast->command = pTD1->command;
    pTDLast->lastTDofTransaction = pTD1->lastTDofTransaction;

    pTDLast->_logicalNext = pTD1->_logicalNext;
    pTDLast->logicalBuffer = pTD1->logicalBuffer;

    // Point end of new TDs to first TD, now new tail
    pTD->SetPhysicalLink(pTD1->GetPhysicalAddrWithType());
    pTD->_logicalNext = pTD1;
    
    // squash pointers on new tail
    pTD1->SetPhysicalLink(kUHCI_TD_T);
	pTD1->GetSharedLogical()->token = HostToUSBLong(myToggle);							// the last - invalid TD - contains the correct toggle for the next TD
	pTD1->GetSharedLogical()->buffer = 0;												// smash the buffer
    pTD1->_logicalNext = NULL;
    USBLog(7, "AppleUSBUHCI[%p]::AllocTDChain - zeroing out command in  TD (%p)", this, pTD1);
    pTD1->command = NULL;
    
    pQH->lastTD = pTD1;
    pTDLast->GetSharedLogical()->ctrlStatus = ctrlStatus;
	USBLog(7, "AllocTDChain - TD list for QH %p firstTD %p lastTD %p ================================================", pQH, pQH->firstTD, pQH->lastTD);
	pTD = pQH->firstTD;
	while (pTD)
	{
		pTD->print(7);
		pTD = (AppleUHCITransferDescriptor*)pTD->_logicalNext;
	}
	USBLog(7, "AllocTDChain - end of chain =========================================================");
    if (status)
    {
		USBLog(2, "AppleUSBUHCI[%p]::AllocTDChain: returning status 0x%x", this, status);
    }
	
	if ((pQH->type == kUSBControl) || (pQH->type == kUSBBulk))
	{
		if (!_controlBulkTransactionsOut)
		{
			UInt32 link;
			link = _lastQH->GetPhysicalLink();
			USBLog(6, "AppleUSBUHCI[%p]::AllocTDChain - first transaction - unblocking list (%p to %p)", this, (void*)link, (void*)(link & ~kUHCI_QH_T));
			_lastQH->SetPhysicalLink(link & ~kUHCI_QH_T);
		}
		_controlBulkTransactionsOut++;
		USBLog(7, "AppleUSBUHCI[%p]::AllocTDChain - _controlBulkTransactionsOut(%p)", this, (void*)_controlBulkTransactionsOut);
	}
    return status;
}



void 
AppleUSBUHCI::AddIsochFramesToSchedule(IOUSBControllerIsochEndpoint* pEP)
{
    UInt64									currFrame, startFrame, finFrame;
    IOUSBControllerIsochListElement			*pTD = NULL;
    UInt16									nextSlot, firstOutSlot;
    AbsoluteTime							timeStamp;
	
    if(pEP->toDoList == NULL)
    {
		USBLog(7, "AppleUSBUHCI[%p]::AddIsochFramesToSchedule - no frames to add fn:%d EP:%d", this, pEP->functionAddress, pEP->endpointNumber);
		return;
    }
    if(pEP->aborting)
    {
		USBLog(1, "AppleUSBUHCI[%p]::AddIsochFramesToSchedule - EP (%p) is aborting - not adding", this, pEP);
		return;
    }

	// 4211382 - This routine is already non-reentrant, since it runs on the WL.
	// However, we also need to disable preemption while we are in here, since we have to get everything
	// done within a couple of milliseconds, and if we are preempted, we may come back long after that
	// point. So take a SimpleLock to prevent preemption
	if (!IOSimpleLockTryLock(_isochScheduleLock))
	{
		// This would indicate reentrancy, which should never ever happen
		USBError(1, "AppleUSBUHCI[%p]::AddIsochFramesToSchedule - could not obtain scheduling lock", this);
		return;
	}
	
	//*******************************************************************************************************
	// ************* WARNING WARNING WARNING ****************************************************************
	// Preemption is now off, which means that we cannot make any calls which may block
	// So don't call USBLog, and don't call for example
	//*******************************************************************************************************
	
    // Don't get GetFrameNumber() unless we're going to use it
    //
    currFrame = GetFrameNumber();
	startFrame = currFrame;
    
    // USBLog(7, "AppleUSBUHCI[%p]::AddIsochFramesToSchedule - fn:%d EP:%d inSlot (0x%x), currFrame: 0x%Lx", this, pEP->functionAddress, pEP->endpointNumber, pEP->inSlot, currFrame);
    clock_get_uptime(&timeStamp);
	USBLog(7, "AddIsochFramesToSchedule - first Todo frame (%Ld), currFrame+1(%Ld)", pEP->toDoList->_frameNumber, (currFrame+1));
    while(pEP->toDoList->_frameNumber <= (currFrame+1))		// Add 1, and use <= so you never put in a new frame 
															// at less than 2 ahead of now.
    {
		IOReturn	ret;
		UInt64		newCurrFrame;
		
		// this transaction is old before it began, move to done queue
		pTD = GetTDfromToDoList(pEP);
		//USBLog(7, "AppleUSBUHCI[%p]::AddIsochFramesToSchedule - ignoring TD(%p) because it is too old (%Lx) vs (%Lx) ", this, pTD, pTD->_frameNumber, currFrame);
		ret = pTD->UpdateFrameList(timeStamp);		// TODO - accumulate the return values
		if (pEP->scheduledTDs)
			PutTDonDeferredQueue(pEP, pTD);
		else
		{
			//USBLog(7, "AppleUSBUHCI[%p]::AddIsochFramesToSchedule - putting TD(%p) on Done Queue instead of Deferred Queue ", this, pTD);
			PutTDonDoneQueue(pEP, pTD, true);
		}
	    
        //USBLog(7, "AppleUSBUHCI[%p]::AddIsochFramesToSchedule - pTD = %p", this, pTD);
		if(pEP->toDoList == NULL)
		{	
			// Run out of transactions to move.  Call this on a separate thread so that we return to the caller right away
            // 
			// ReturnIsocDoneQueue(pEP);
			IOSimpleLockUnlock(_isochScheduleLock);
			// OK to call USBLog, now that preemption is reenabled
            USBLog(7, "AppleUSBUHCI[%p]::AddIsochFramesToSchedule - calling the ReturnIsocDoneQueue on a separate thread", this);
            thread_call_enter1(_returnIsochDoneQueueThread, (thread_call_param_t) pEP);
			return;
		}
		newCurrFrame = GetFrameNumber();
		if (newCurrFrame > currFrame)
		{
			//USBLog(1, "AppleUSBUHCI[%p]::AddIsochFramesToSchedule - Current frame moved (0x%Lx->0x%Lx) resetting", this, currFrame, newCurrFrame);
			currFrame = newCurrFrame;
		}		
    }
    
	firstOutSlot = (currFrame+1) & kUHCI_NVFRAMES_MASK;							// this will be used if the _outSlot is not yet initialized
	
    currFrame = pEP->toDoList->_frameNumber;									// start looking at the first available number
	
    // This needs to be fixed up when we have variable length lists.
    pEP->inSlot = currFrame & kUHCI_NVFRAMES_MASK;
	
    do
    {		
		nextSlot = (pEP->inSlot + 1) & kUHCI_NVFRAMES_MASK;
		if (pEP->inSlot == _outSlot)
		{
			//USBLog(2, "AppleUSBUHCI[%p]::AddIsochFramesToSchedule - caught up pEP->inSlot (0x%x) _outSlot (0x%x)", this, pEP->inSlot, _outSlot);
			break;
		}
		if( nextSlot == _outSlot) 								// weve caught up with our tail
		{
			//USBLog(2, "AppleUSBUHCI[%p]::AddIsochFramesToSchedule - caught up nextSlot (0x%x) _outSlot (0x%x)", this, nextSlot, _outSlot);
			break;
		}
		
		pTD = GetTDfromToDoList(pEP);
		//USBLog(7, "AppleUSBUHCI[%p]::AddIsochFramesToSchedule - checking TD(%p) FN(0x%Lx) against currFrame (0x%Lx)", this, pTD, pTD->_frameNumber, currFrame);
		
		if(currFrame == pTD->_frameNumber)
		{			
			if(_outSlot > kUHCI_NVFRAMES)
			{
				_outSlot = firstOutSlot;
			}
			// Place TD in list
			USBLog(7, "AppleUSBUHCI[%p]::AddIsochFramesToSchedule - linking TD (%p) with frame (0x%Ld) into slot (0x%x) - curr next log (%p) phys (%p)", this, pTD, pTD->_frameNumber, pEP->inSlot, _logicalFrameList[pEP->inSlot], (void*)USBToHostLong(_frameList[pEP->inSlot]));
			pTD->print(7);
			
			pTD->SetPhysicalLink(USBToHostLong(_frameList[pEP->inSlot]));
			pTD->_logicalNext = _logicalFrameList[pEP->inSlot];
			_logicalFrameList[pEP->inSlot] = pTD;
			_frameList[pEP->inSlot] = HostToUSBLong(pTD->GetPhysicalAddrWithType());			
			pEP->scheduledTDs++;
			//USBLog(7, "AppleUSBUHCI[%p]::AddIsochFramesToSchedule - _frameList[%x]:%x", this, pEP->inSlot, USBToHostLong(_frameList[pEP->inSlot]));
		}
		else
		{
			USBError(1, "AppleUSBUHCI[%p]::AddIsochFramesToSchedule - expected frame (%qd) and see frame (%qd) - should do something here!!", this, currFrame, pTD->_frameNumber);
		}
		currFrame++;
		pEP->inSlot = nextSlot;
		
		// USBLog(7, "AppleUSBUHCI[%p]::AddIsochFramesToSchedule - pEP->inSlot is now 0x%x", this, pEP->inSlot);	
    } while(pEP->toDoList != NULL);
	
	finFrame = GetFrameNumber();
	// Unlock, reenable preemption, so we can log
	IOSimpleLockUnlock(_isochScheduleLock);
	if ((finFrame - startFrame) > 1)
		USBError(1, "AppleUSBUHCI[%p]::AddIsochFramesToSchedule - end -  startFrame(0x%Ld) finFrame(0x%Ld)", this, startFrame, finFrame);
    USBLog(7, "AppleUSBUHCI[%p]::AddIsochFramesToSchedule - finished,  currFrame: %Ld", this, GetFrameNumber() );
}



IOReturn 
AppleUSBUHCI::AbortIsochEP(IOUSBControllerIsochEndpoint* pEP)
{
    UInt32								slot;
    IOReturn							err;
    IOUSBControllerIsochListElement		*pTD;
    AbsoluteTime						timeStamp;
    
	if (pEP->deferredQueue || pEP->toDoList || pEP->doneQueue || pEP->activeTDs || pEP->onToDoList || pEP->scheduledTDs || pEP->deferredTDs || pEP->onReversedList || pEP->onDoneQueue)
		USBLog(6, "+AppleUSBUHCI[%p]::AbortIsochEP[%p] - start - _outSlot (0x%x) pEP->inSlot (0x%x) activeTDs (%ld) onToDoList (%ld) todo (%p) deferredTDs (%ld) deferred(%p) scheduledTDs (%ld) onProducerQ (%ld) consumer (%ld) producer (%ld) onReversedList (%ld) onDoneQueue (%ld)  doneQueue (%p)", this, pEP,  _outSlot, pEP->inSlot, pEP->activeTDs, pEP->onToDoList, pEP->toDoList, pEP->deferredTDs, pEP->deferredQueue, pEP->scheduledTDs, pEP->onProducerQ, _consumerCount, _producerCount, pEP->onReversedList, pEP->onDoneQueue, pEP->doneQueue);
	
    USBLog(7, "+AppleUSBUHCI[%p]::AbortIsochEP (%p)", this, pEP);
	
    // we need to make sure that the interrupt routine is not processing the periodic list
	_inAbortIsochEP = true;
	pEP->aborting = true;
	
    // DisablePeriodicSchedule();
    clock_get_uptime(&timeStamp);
	
    // now make sure we finish any periodic processing we were already doing (for MP machines)
    while (_filterInterruptActive)
		;


	// now scavange any transactions which were already on the done queue
    err = scavengeIsochTransactions();
	
	
    if (err)
    {
		USBLog(1, "AppleUSBUHCI[%p]::AbortIsochEP - err (0x%x) from scavengeIsochTransactions", this, err);
    }
    
	if (pEP->deferredQueue || pEP->toDoList || pEP->doneQueue || pEP->activeTDs || pEP->onToDoList || pEP->scheduledTDs || pEP->deferredTDs || pEP->onReversedList || pEP->onDoneQueue)
		USBLog(6, "+AppleUSBUHCI[%p]::AbortIsochEP[%p] - after scavenge - _outSlot (0x%x) pEP->inSlot (0x%x) activeTDs (%ld) onToDoList (%ld) todo (%p) deferredTDs (%ld) deferred(%p) scheduledTDs (%ld) onProducerQ (%ld) consumer (%ld) producer (%ld) onReversedList (%ld) onDoneQueue (%ld)  doneQueue (%p)", this, pEP,  _outSlot, pEP->inSlot, pEP->activeTDs, pEP->onToDoList, pEP->toDoList, pEP->deferredTDs, pEP->deferredQueue, pEP->scheduledTDs, pEP->onProducerQ, _consumerCount, _producerCount, pEP->onReversedList, pEP->onDoneQueue, pEP->doneQueue);
	
    if ((_outSlot < kUHCI_NVFRAMES) && (pEP->inSlot < kUHCI_NVFRAMES))
    {
		bool								stopAdvancing = false;
		IOUSBControllerIsochListElement		*activeTD = NULL;
		
        // now scavenge the remaining transactions on the periodic list
        slot = _outSlot;
        while (slot != pEP->inSlot)
        {
            IOUSBControllerListElement 		*thing;
            IOUSBControllerListElement		*nextThing;
            IOUSBControllerListElement		*prevThing;
			UInt32							nextSlot;
            
			nextSlot = (slot+1) & kUHCI_NVFRAMES_MASK;
            thing = _logicalFrameList[slot];
            prevThing = NULL;
			if (thing == NULL && (nextSlot != pEP->inSlot))
				_outSlot = nextSlot;
            while (thing != NULL)
            {
                nextThing = thing->_logicalNext;
                pTD = OSDynamicCast(IOUSBControllerIsochListElement, thing);
                if(pTD)
                {
                    if (pTD->_pEndpoint == pEP)
                    {
						UInt16		frSlot = (ReadFrameNumber() & kUHCI_NVFRAMES_MASK);
						
                        // first unlink it
                        if (prevThing)
                        {
                            prevThing->_logicalNext = thing->_logicalNext;
                            prevThing->SetPhysicalLink(thing->GetPhysicalLink());
							thing = prevThing;															// to cause prevThing to remain unchanged at the bottom of the loop
                        }
                        else
                        {
                            _logicalFrameList[slot] = nextThing;
                            _frameList[slot] = HostToUSBLong(thing->GetPhysicalLink());
							thing = NULL;																// to cause prevThing to remain unchanged (NULL) at the bottom of the loop
							if (nextThing == NULL)
							{
								if (!stopAdvancing)
								{
									USBLog(5, "AppleUSBUHCI[%p]::AbortIsochEP(%p) - advancing _outslot from 0x%x to 0x%lx", this, pEP, _outSlot, nextSlot);
									_outSlot = nextSlot;
								}
								else
								{
									USBLog(5, "AppleUSBUHCI[%p]::AbortIsochEP(%p) - would have advanced _outslot from 0x%x to 0x%lx", this, pEP, _outSlot, nextSlot);
								}
							}
                        }
						// before we make a change to the recently unlinked thing (pTD)
						// we need to make sure that the controller is not actually looking at it
						
						if (frSlot == slot)
						{
							// be very very careful here - we don't want to confuse the controller
							// IODelay(1000);																// need to delay (NOT SLEEP) for 1 ms to make sure that we go to the next frame
							// we used to delay as above. the problem with that is that it caused a 1ms delay for every list element we found
							// on consecutive frames so now, we just need to remember that we have one element whose software link and hardware link
							// may be inconsistent, and we need to delay to let the processer get it out of its cache
							if (activeTD)
							{
								// this is not the first one we have seen this trip. update the last one we saw and save this one
								// we can do this because there is only on TD per EP on the list, so the old one is no longer being examined by the controller
								activeTD->UpdateFrameList(timeStamp);
							}
							// remember it so we can update the frame list when we are done
							activeTD = pTD;
						}
						else
						{
							err = pTD->UpdateFrameList(timeStamp);
						}
						pEP->scheduledTDs--;
                        PutTDonDoneQueue(pEP, pTD, true	);
                    }
					else if (pTD->_pEndpoint == NULL)
					{
						USBLog(1, "AppleUSBUHCI[%p]::AbortIsochEP (%p) - NULL endpoint in pTD %p", this, pEP, pTD);
					}
					else
					{
						stopAdvancing = true;
						USBLog(7, "AppleUSBUHCI[%p]::AbortIsochEP (%p) - a different EP in play (%p) - stop advancing", this, pEP, pTD->_pEndpoint);
					}
                }
                else 
                {
                    // only care about Isoch in this list - if we get here we are at the Interrupt list, which means no more Isoch
                    break;
                }
                prevThing = thing;
                thing = nextThing;
            }
            slot = nextSlot;
        }
		if (activeTD)
		{
			// delay 1ms to make sure the controller is no longer tounching it before we update the frame list
			// note that it is already on the done queue
			IODelay(1000);
			activeTD->UpdateFrameList(timeStamp);
		}
    }
    
    // now transfer any transactions from the todo list to the done queue
    pTD = GetTDfromToDoList(pEP);
    while (pTD)
    {
		err = pTD->UpdateFrameList(timeStamp);
		PutTDonDoneQueue(pEP, pTD, true);
		pTD = GetTDfromToDoList(pEP);
    }
	
	if (!pEP->scheduledTDs)
	{
		// since we have no Isoch xactions on the endpoint, we can reset the counter
		pEP->firstAvailableFrame = 0;
		pEP->inSlot = kUHCI_NVFRAMES + 1;    
	}
    // we can go back to processing now
	_inAbortIsochEP = false;
    // EnablePeriodicSchedule();
    
    pEP->accumulatedStatus = kIOReturnAborted;
    ReturnIsochDoneQueue(pEP);
    pEP->accumulatedStatus = kIOReturnSuccess;
	if (pEP->deferredQueue || pEP->toDoList || pEP->doneQueue || pEP->activeTDs || pEP->onToDoList || pEP->scheduledTDs || pEP->deferredTDs || pEP->onReversedList || pEP->onDoneQueue)
	{
		USBLog(1, "+AppleUSBUHCI[%p]::AbortIsochEP[%p] - done - _outSlot (0x%x) pEP->inSlot (0x%x) activeTDs (%ld) onToDoList (%ld) todo (%p) deferredTDs (%ld) deferred(%p) scheduledTDs (%ld) onProducerQ (%ld) consumer (%ld) producer (%ld) onReversedList (%ld) onDoneQueue (%ld)  doneQueue (%p)", this, pEP,  _outSlot, pEP->inSlot, pEP->activeTDs, pEP->onToDoList, pEP->toDoList, pEP->deferredTDs, pEP->deferredQueue, pEP->scheduledTDs, pEP->onProducerQ, _consumerCount, _producerCount, pEP->onReversedList, pEP->onDoneQueue, pEP->doneQueue);
	}
	else
	{
		USBLog(6, "-AppleUSBUHCI[%p]::AbortIsochEP[%p] - done - all clean - _outSlot (0x%x), pEP->inSlot (0x%x)", this, pEP, _outSlot, pEP->inSlot);
	}
	pEP->aborting = false;
    USBLog(7, "-AppleUSBUHCI[%p]::AbortIsochEP (%p)", this, pEP);
    return kIOReturnSuccess;
}
