Source to iokit/Drivers/usb/drvAppleOHCI/AppleOHCI_UIM.cpp


Enter a symbol's name here to quickly find it.

/*
 * Copyright (c) 1998-2000 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.1 (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 OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 * Copyright (c) 1998 Apple Computer, Inc.  All rights reserved.
 *
 * HISTORY
 *
 */


#include <libkern/OSByteOrder.h>

#include "AppleOHCI.h"
#include <IOKit/IOMemoryDescriptor.h>
#include <IOKit/IOMemoryCursor.h>

#define DEBUGGING_LEVEL 0	// 1 = low; 2 = high; 3 = extreme
#define DEBUGLOG IOLog
#define nil (0)
#define kP_UIMName ""

#if DEBUGGING_LEVEL > 0
#define USBExpertStatusLevel(a, b, c, d) DEBUGLOG("UIM: %s 0x%lx\n", c, (UInt32)(d))
#else
#define USBExpertStatusLevel(a, b, c, d)
#endif

#define super IOUSBController

void print_td(OHCIGeneralTransferDescriptorPtr x);
void print_itd(OHCIIsochTransferDescriptorPtr x);
void print_ed(OHCIEndpointDescriptorPtr x);
void print_isoc_ed(OHCIEndpointDescriptorPtr x);
void print_list(OHCIEndpointDescriptorPtr pListHead,
                OHCIEndpointDescriptorPtr pListTail);
void print_control_list(OHCIUIMDataPtr pData);
void print_bulk_list(OHCIUIMDataPtr pData);
void print_int_list(OHCIUIMDataPtr pData);

static inline OHCIEDFormat
GetEDType(OHCIEndpointDescriptorPtr pED)
{
    return ((OSReadLittleInt32(&pED->flags, 0) & kOHCIEDControl_F) >>
						kOHCIEDControl_FPhase);
}

IOReturn AppleOHCI::UIMCreateGeneralTransfer(
            OHCIEndpointDescriptorPtr		queue,
            IOUSBCompletion			completion,
            IOMemoryDescriptor *		CBP,
            UInt32				bufferSize,
            UInt32				flags,
            UInt32				type,
            UInt32				kickBits)
{
    OHCIRegistersPtr			pOHCIRegisters;
    OHCIGeneralTransferDescriptorPtr	pOHCIGeneralTransferDescriptor,
                                        newOHCIGeneralTransferDescriptor;
    IOReturn				status = kIOReturnSuccess;
    IOPhysicalSegment      			physicalAddresses[2];	
    IOByteCount				transferOffset;
    UInt32				pageSize;
    UInt32				pageCount;

    pOHCIRegisters = pOHCIUIMData->pOHCIRegisters;
    pageSize = pOHCIUIMData->pageSize;

    // Handy for debugging transfer lists
    flags |= (kOHCIGTDConditionNotAccessed << kOHCIGTDControl_CCPhase);

// FERG DEBUG
// uncomment the next line to force the data to be put in TD list, but not be processed
// this is handy for using USBProber/Macsbug to look at TD's to see if they're OK.
// pEDQueue->dWord0 |= OSSwapInt32 (kOHCIEDControl_K);
// FERG DEBUG
    if (bufferSize != 0)
    {
        transferOffset = 0;
        while (transferOffset < bufferSize)
        {
            if(pOHCIUIMData->errataBits & kErrataOnlySinglePageTransfers)
                pageCount = _genCursor->getPhysicalSegments(CBP, transferOffset, physicalAddresses, 1);
            else
                pageCount = _genCursor->getPhysicalSegments(CBP, transferOffset, physicalAddresses, 2);
            newOHCIGeneralTransferDescriptor = OHCIUIMAllocateTD();
            if (newOHCIGeneralTransferDescriptor == nil) {
                status = kIOReturnNoMemory;
                break;
            }
            pOHCIGeneralTransferDescriptor = 
		(OHCIGeneralTransferDescriptorPtr)queue->pLogicalTailP;
            OSWriteLittleInt32(&pOHCIGeneralTransferDescriptor->flags, 0, flags);
            OSWriteLittleInt32(&pOHCIGeneralTransferDescriptor->currentBufferPtr, 0,
                physicalAddresses[0].location);
            OSWriteLittleInt32(&pOHCIGeneralTransferDescriptor->nextTD, 0,
		newOHCIGeneralTransferDescriptor->pPhysical);
            if (pageCount == 2) {
                OSWriteLittleInt32(&pOHCIGeneralTransferDescriptor->bufferEnd, 0,
                        physicalAddresses[1].location + physicalAddresses[1].length - 1);
                transferOffset += physicalAddresses[1].length;
            }
            else
                OSWriteLittleInt32(&pOHCIGeneralTransferDescriptor->bufferEnd, 0,
                        physicalAddresses[0].location + physicalAddresses[0].length - 1);

            pOHCIGeneralTransferDescriptor->pLogicalNext =
                newOHCIGeneralTransferDescriptor;
            pOHCIGeneralTransferDescriptor->pEndpoint = queue;
            pOHCIGeneralTransferDescriptor->pType = type;
            transferOffset += physicalAddresses[0].length;

            // only supply a callback when the entire buffer has been
            // transfered.
            if (transferOffset >= bufferSize)
                pOHCIGeneralTransferDescriptor->completion = completion;
            else
                pOHCIGeneralTransferDescriptor->completion.action = nil;
            queue->tdQueueTailPtr = pOHCIGeneralTransferDescriptor->nextTD;
            queue->pLogicalTailP =
                newOHCIGeneralTransferDescriptor;
            OSWriteLittleInt32(&pOHCIRegisters->hcCommandStatus, 0, kickBits);
        }
    }
    else
    {
        newOHCIGeneralTransferDescriptor = OHCIUIMAllocateTD();
        // last in queue is dummy descriptor. Fill it in then add new dummy
        pOHCIGeneralTransferDescriptor =
            (OHCIGeneralTransferDescriptorPtr) queue->pLogicalTailP;

        OSWriteLittleInt32(&pOHCIGeneralTransferDescriptor->flags, 0, flags);
        OSWriteLittleInt32(&pOHCIGeneralTransferDescriptor->nextTD, 0,
            newOHCIGeneralTransferDescriptor->pPhysical);
        pOHCIGeneralTransferDescriptor->pLogicalNext =
            newOHCIGeneralTransferDescriptor;
        pOHCIGeneralTransferDescriptor->pEndpoint = queue;
        pOHCIGeneralTransferDescriptor->pType = type;

        /* for zero sized buffers */
        pOHCIGeneralTransferDescriptor->currentBufferPtr = 0;
        pOHCIGeneralTransferDescriptor->bufferEnd = 0;
        pOHCIGeneralTransferDescriptor->completion = completion;

        /* Make new descriptor the tail */
        queue->tdQueueTailPtr = pOHCIGeneralTransferDescriptor->nextTD;
        queue->pLogicalTailP = newOHCIGeneralTransferDescriptor;
        OSWriteLittleInt32(&pOHCIRegisters->hcCommandStatus, 0, kickBits);
    }

#if (DEBUGGING_LEVEL > 0)
    print_td(pOHCIGeneralTransferDescriptor);
    DEBUGLOG("UIMCreateGeneralTransfer: returning status=%d\n", status);
#endif
    return (status);
}

IOReturn AppleOHCI::UIMCreateControlEndpoint(
            UInt8				functionAddress,
            UInt8				endpointNumber,
            UInt16				maxPacketSize,
            UInt8				speed)
{
    OHCIEndpointDescriptorPtr	pOHCIEndpointDescriptor, pED;

#if (DEBUGGING_LEVEL > 0)
    DEBUGLOG("%s: UIMCreateControlEndpoint(%d, %d, %d, %d)\n", getName(),
          functionAddress, endpointNumber, maxPacketSize, speed);
#endif
    if (pOHCIUIMData->rootHubFuncAddress == functionAddress)
    {
        return(kIOReturnSuccess);
    }
        
    pED = (OHCIEndpointDescriptorPtr) pOHCIUIMData->pControlHead;
    if ((speed == kOHCIEDSpeedFull) && pOHCIUIMData->OptiOn)
        pED = (OHCIEndpointDescriptorPtr) pOHCIUIMData->pBulkHead;

    pOHCIEndpointDescriptor = AddEmptyEndPoint(functionAddress,
                                               endpointNumber,
                                               maxPacketSize,
                                               speed,
                                               kOHCIEDDirectionTD,
                                               pED,
                                               kOHCIEDFormatGeneralTD);

#if (DEBUGGING_LEVEL > 2)
    if ((speed == kOHCIEDSpeedFull) && pOHCIUIMData->OptiOn)
        print_bulk_list(pOHCIUIMData);
    else
	print_control_list(pOHCIUIMData);
#endif

    if (pOHCIEndpointDescriptor == nil)
        return(-1);  //FIXME
    return (kIOReturnSuccess);
}


IOReturn AppleOHCI::UIMCreateControlTransfer(
            short				functionAddress,
            short				endpointNumber,
            IOUSBCompletion			completion,
            IOMemoryDescriptor *		CBP,
            bool				bufferRounding,
            UInt32				bufferSize,
            short				direction)
{
    UInt32				myBufferRounding;
    UInt32				myDirection;
    UInt32				myToggle;
    OHCIEndpointDescriptorPtr		pEDQueue, pEDDummy;
    IOReturn				status;

#if (DEBUGGING_LEVEL > 0)
    DEBUGLOG("\tCrntlTx: adr=%d:%d cbp=%lx:%lx br=%s cback=[%lx:%lx] dir=%d)\n",
          functionAddress, endpointNumber, CBP, bufferSize,
          bufferRounding?"YES":"NO",
             (UInt32)completion.target, (UInt32)completion.parameter, direction);
#endif

    if (direction == kUSBOut)
    {
        direction = kOHCIGTDPIDOut;
    }
    else if (direction == kUSBIn)
    {
        direction = kOHCIGTDPIDIn;
    }
    else
    {
        direction = kOHCIGTDPIDSetup;
    }
    // search for endpoint descriptor

    pEDQueue = FindControlEndpoint(functionAddress, endpointNumber, &pEDDummy);
    if (pEDQueue == nil)
    {
        IOLog("UIMCreateControlTransfer: Could not find endpoint\n");
        return(kIOUSBEndpointNotFound);
    }
    myBufferRounding = (UInt32) bufferRounding << kOHCIBufferRoundingOffset;
    myDirection = (UInt32) direction << kOHCIDirectionOffset;
    myToggle = kOHCIBit25;	/* Take data toggle from TD */
    if (direction != 0)
    {
        /* Setup uses Data 0, data status use Data1 */
        myToggle |= kOHCIBit24;	/* use Data1 */
    }

    status = UIMCreateGeneralTransfer(pEDQueue, completion, CBP, bufferSize,
	myBufferRounding | myDirection | myToggle, kOHCIControlSetupType, kOHCIHcCommandStatus_CLF);

#if (DEBUGGING_LEVEL > 2)
    print_ed(pEDQueue);
#endif

    return (status);
}

IOReturn AppleOHCI::UIMCreateControlTransfer(
            short				functionAddress,
            short				endpointNumber,
            IOUSBCompletion			completion,
            void *				CBP,
            bool				bufferRounding,
            UInt32				bufferSize,
            short				direction)
{
    IOMemoryDescriptor *		desc = NULL;
    IODirection				descDirection;
    IOReturn				status;

#if (DEBUGGING_LEVEL > 0)
    DEBUGLOG("\tCrntlTx: adr=%d:%d cbp=%lx:%lx br=%s cback=[%lx:%lx] dir=%d)\n",
          functionAddress, endpointNumber, CBP, bufferSize,
          bufferRounding?"YES":"NO",
             (UInt32)completion.target, (UInt32)completion.parameter, direction);
#endif
    if (direction == kUSBOut)
    {
        descDirection = kIODirectionOut;
    }
    else if (direction == kUSBIn)
    {
        descDirection = kIODirectionIn;
    }
    else
    {
        descDirection = kIODirectionOut;
    }
    if(bufferSize != 0) {
        desc = IOMemoryDescriptor::withAddress(CBP, bufferSize, descDirection);
        if(!desc)
            return(kIOReturnNoMemory);
    }

    status = UIMCreateControlTransfer(functionAddress, endpointNumber, completion,
		desc, bufferRounding, bufferSize, direction);

    if(desc)
        desc->release();

    return (status);
}

/* Not implemented - use UIMAbortEndpoint
IOReturn AppleOHCI::UIMAbortControlEndpoint(void);
IOReturn AppleOHCI::UIMEnableControlEndpoint(void);
IOReturn AppleOHCI::UIMDisableControlEndpoint(void);
*/

// Bulk
IOReturn AppleOHCI::UIMCreateBulkEndpoint(
            UInt8				functionAddress,
            UInt8				endpointNumber,
            UInt8				direction,
            UInt8				speed,
            UInt8				maxPacketSize)
{
    OHCIEndpointDescriptorPtr	pOHCIEndpointDescriptor, pED;


#if (DEBUGGING_LEVEL > 0)
    DEBUGLOG("%s: UIMCreateBulkEndpoint(adr=%d:%d, max=%d, dir=%d)\n", getName(),
          functionAddress, endpointNumber, maxPacketSize, direction);
#endif
    
    if (direction == kUSBOut)
            direction = kOHCIEDDirectionOut;
    else if (direction == kUSBIn)
            direction = kOHCIEDDirectionIn;
    else
            direction = kOHCIEDDirectionTD;

    pED = (OHCIEndpointDescriptorPtr) pOHCIUIMData->pBulkHead;
    pOHCIEndpointDescriptor = AddEmptyEndPoint (functionAddress,
                                                endpointNumber,
                                                maxPacketSize,
                                                speed,
                                                direction,
                                                pED,
                                                kOHCIEDFormatGeneralTD);
    if (pOHCIEndpointDescriptor == nil)
        return(kIOReturnNoMemory);

    return (kIOReturnSuccess);
}

IOReturn AppleOHCI::UIMCreateBulkTransfer(
            short				functionAddress,
            short				endpointNumber,
            IOUSBCompletion			completion,
            IOMemoryDescriptor *		CBP,
            bool				bufferRounding,
            UInt32				bufferSize,
            short				direction)
{
    IOReturn				status = kIOReturnSuccess;
    UInt32				myBufferRounding;
    UInt32				TDDirection;
    UInt32				kickBits;
    OHCIEndpointDescriptorPtr		pEDQueue, pEDDummy;

#if (DEBUGGING_LEVEL > 0)
    DEBUGLOG("\tBulkTx: adr=%d:%d cbp=%lx:%x br=%s cback=[%lx:%lx:%lx] dir=%d)\n",
	functionAddress, endpointNumber, CBP, bufferSize, bufferRounding?"YES":"NO", 
	(UInt32)completion.action, (UInt32)completion.target, (UInt32)completion.parameter, direction);
#endif

    if (direction == kUSBOut)
        direction = kOHCIEDDirectionOut;
    else if (direction == kUSBIn)
        direction = kOHCIEDDirectionIn;
    else
        direction = kOHCIEDDirectionTD;

    // search for endpoint descriptor
    pEDQueue =
        FindBulkEndpoint(functionAddress, endpointNumber, direction, &pEDDummy);

    if (!pEDQueue)
    {
        IOLog("UIMCreateBulkTransfer: Could not find endpoint\n");
        return (kIOUSBEndpointNotFound);
    }

    myBufferRounding = (UInt32) bufferRounding << kOHCIBufferRoundingOffset;
    TDDirection = (UInt32) direction << kOHCIDirectionOffset;
    kickBits = kOHCIHcCommandStatus_BLF;
    if ( pOHCIUIMData->OptiOn)
        kickBits |= kOHCIHcCommandStatus_CLF;		

    status = UIMCreateGeneralTransfer(pEDQueue, completion, CBP, bufferSize,
	myBufferRounding | TDDirection, kOHCIBulkTransferOutType, kickBits);

#if (DEBUGGING_LEVEL > 2)
    print_bulk_list(pOHCIUIMData);
#endif

    return (status);
}

// Interrupt
IOReturn AppleOHCI::UIMCreateInterruptEndpoint(
            short				functionAddress,
            short				endpointNumber,
            UInt8				direction,
            short				speed,
            UInt16				maxPacketSize,
            short				pollingRate)
{
    OHCIEndpointDescriptorPtr		pOHCIEndpointDescriptor;
    OHCIEndpointDescriptorPtr		pED;
    OHCIIntHeadPtr			pInterruptHead;
    int					offset;


#if (DEBUGGING_LEVEL > 0)
    DEBUGLOG("%s: UIMCreateInterruptEndpoint (%d, %d, %s, %d, %d)\n", getName(),
          functionAddress, endpointNumber, speed ? "lo" : "hi", maxPacketSize,
          pollingRate);
#endif

    if (pOHCIUIMData->rootHubFuncAddress == functionAddress)
    {
        return(kIOReturnSuccess);
    }

    if (direction == kUSBOut)
            direction = kOHCIEDDirectionOut;
    else if (direction == kUSBIn)
            direction = kOHCIEDDirectionIn;
    else
            direction = kOHCIEDDirectionTD;

    pInterruptHead = pOHCIUIMData->pInterruptHead;

///ZZZZz  opti bug fix!!!!
    if (pOHCIUIMData->OptiOn)
            if (speed == kOHCIEDSpeedFull)
                    if (pollingRate >= 8)
                            pollingRate = 7;

    // Do we have room?? if so return with offset equal to location
    if (DetermineInterruptOffset(pollingRate, maxPacketSize, &offset) == false)
        return(kIOReturnNoBandwidth);

#if (DEBUGGING_LEVEL > 0)
    DEBUGLOG("%s: UIMCreateInterruptEndpoint: offset = %d\n", getName(), offset);
#endif
    pED = (OHCIEndpointDescriptorPtr) pInterruptHead[offset].pHead;
    pOHCIEndpointDescriptor = AddEmptyEndPoint (functionAddress, endpointNumber, 
			maxPacketSize, speed, direction, pED, kOHCIEDFormatGeneralTD);
    if (nil == pOHCIEndpointDescriptor)
            return(-1);

    pInterruptHead[offset].nodeBandwidth += maxPacketSize;

#if (DEBUGGING_LEVEL > 2)
    print_int_list(pOHCIUIMData);
#endif

    return (kIOReturnSuccess);
}

IOReturn AppleOHCI::UIMCreateInterruptTransfer(
            short				functionAddress,
            short				endpointNumber,
            IOUSBCompletion			completion,
            IOMemoryDescriptor *		CBP,
            bool				bufferRounding,
            UInt32				bufferSize,
            short				direction)
{
    IOReturn				status = kIOReturnSuccess;
    UInt32				myBufferRounding;
    UInt32				myDirection;
    UInt32				myToggle;
    OHCIEndpointDescriptorPtr		pEDQueue, temp;
    OHCIIntHeadPtr			pInterruptHead;

#if (DEBUGGING_LEVEL > 0)
    DEBUGLOG("\tIntTx: adr=%d:%d cbp=%lx:%lx br=%s cback=[%lx:%lx:%lx])\n", functionAddress, endpointNumber, CBP, bufferSize, bufferRounding?"YES":"NO", (UInt32)completion.action, (UInt32)completion.target, (UInt32)completion.parameter);
#endif

    if (pOHCIUIMData->rootHubFuncAddress == functionAddress)
    {
        SimulateRootHubInt(endpointNumber, CBP, bufferSize, completion);
        return(kIOReturnSuccess);
    }

    if (direction == kUSBOut)
        direction = kOHCIEDDirectionOut;
    else if (direction == kUSBIn)
        direction = kOHCIEDDirectionIn;
    else
        direction = kOHCIEDDirectionTD;

    pInterruptHead = pOHCIUIMData->pInterruptHead;

    pEDQueue = FindInterruptEndpoint(functionAddress, endpointNumber,
							direction,&temp);
    if (pEDQueue != nil)
    {
        myBufferRounding = (UInt32) bufferRounding << kOHCIBufferRoundingOffset;
        myToggle = 0;	/* Take data toggle from Endpoint Descriptor */

        myDirection = (UInt32) direction << kOHCIDirectionOffset;

        status = UIMCreateGeneralTransfer(pEDQueue, completion, CBP, bufferSize,
            myBufferRounding | myDirection | myToggle, kOHCIInterruptInType, 0);
    }
    else
    {
        IOLog("UIMCreateInterruptTransfer: Could not find endpoint\n");
        status = kIOUSBEndpointNotFound;
    }
#if (DEBUGGING_LEVEL > 0)
    DEBUGLOG("UIMCreateInterruptTransfer: returning status=%d\n", status);
#endif
    return (status);
}

// Isoch
IOReturn AppleOHCI::UIMCreateIsochEndpoint(
            short				functionAddress,
            short				endpointNumber,
            UInt32				maxPacketSize,
            UInt8				direction)
{
    OHCIEndpointDescriptorPtr	pOHCIEndpointDescriptor, pED;
    UInt32			curMaxPacketSize;
    UInt32			xtraRequest;
    UInt32			edFlags;


    if (direction == kUSBOut)
        direction = kOHCIEDDirectionOut;
    else if (direction == kUSBIn)
        direction = kOHCIEDDirectionIn;
    else
        direction = kOHCIEDDirectionTD;

    pED = FindIsochronousEndpoint(functionAddress, endpointNumber, direction, nil);
    if (pED) {
        // this is the case where we have already created this endpoint, and now we are adjusting the maxPacketSize
        USBExpertStatusLevel (3, 123456789,
		kP_UIMName"IsochEDCreate- Endpoint already exists, checking maxPacketSize ",
		maxPacketSize);
        edFlags = OSReadLittleInt32(&pED->flags, 0);
        curMaxPacketSize = ( edFlags & kOHCIEDControl_MPS) >> kOHCIEDControl_MPSPhase;
        if (maxPacketSize == curMaxPacketSize) {
            USBExpertStatusLevel (3, 123456789,
		kP_UIMName"IsochEDCreate- maxPacketSize the same, no change ",
		maxPacketSize);
            return kIOReturnSuccess;
        }
        if (maxPacketSize > curMaxPacketSize) {
            // client is trying to get more bandwidth
            xtraRequest = maxPacketSize - curMaxPacketSize;
            if (xtraRequest > pOHCIUIMData->isochBandwidthAvail)
            {
                USBExpertStatusLevel (3, 123456789,
			kP_UIMName"IsochEDCreate- out of bandwidth, request (extra) = ",
			xtraRequest);
                USBExpertStatusLevel (3, 123456789,
			kP_UIMName"IsochEDCreate- available = ",
			pOHCIUIMData->isochBandwidthAvail);
                return kIOReturnNoBandwidth;
            }
            pOHCIUIMData->isochBandwidthAvail -= xtraRequest;
        }
        else {
            // client is trying to return some bandwidth
            xtraRequest = curMaxPacketSize - maxPacketSize;
            USBExpertStatusLevel (3, 123456789,
		kP_UIMName"IsochEDCreate- returning some bandwidth: ",
		xtraRequest);
            pOHCIUIMData->isochBandwidthAvail += xtraRequest;
            USBExpertStatusLevel (3, 123456789,
		kP_UIMName"IsochEDCreate- new available = ",
		pOHCIUIMData->isochBandwidthAvail);
        }
        // update the maxPacketSize field in the endpoint
        edFlags &= ~kOHCIEDControl_MPS;					// strip out old MPS
        edFlags |= (maxPacketSize << kOHCIEDControl_MPSPhase);
        OSWriteLittleInt32(&pED->flags, 0, edFlags);
        return kIOReturnSuccess;
    }

    if (maxPacketSize > pOHCIUIMData->isochBandwidthAvail) {
        USBExpertStatusLevel (3, 123456789,
		kP_UIMName"Isoch Endpoint create- no bandwidth, request = ",
		maxPacketSize);
        USBExpertStatusLevel (3, 123456789,
		kP_UIMName"Isoch Endpoint create- available = ",
		pOHCIUIMData->isochBandwidthAvail);
        return kIOReturnNoBandwidth;
    }

    pOHCIUIMData->isochBandwidthAvail -= maxPacketSize;
    pED = pOHCIUIMData->pIsochHead;
    pOHCIEndpointDescriptor = AddEmptyEndPoint(functionAddress, endpointNumber,
	maxPacketSize, kOHCIEDSpeedFull, direction, pED, kOHCIEDFormatIsochronousTD);
    if (pOHCIEndpointDescriptor == nil) {
        pOHCIUIMData->isochBandwidthAvail += maxPacketSize;
        return(kIOReturnNoMemory);
    }

    USBExpertStatusLevel (5, 123456789,
	kP_UIMName"Isoch Endpoint create- success, bandwidth used = ",
	maxPacketSize);
    USBExpertStatusLevel (5, 123456789, 
	kP_UIMName"Isoch Endpoint create- success, new available = ",
	pOHCIUIMData->isochBandwidthAvail);
    return (kIOReturnSuccess);
}


IOReturn AppleOHCI::UIMCreateIsochTransfer(
            short				functionAddress,
            short				endpointNumber,
            IOUSBIsocCompletion			completion,
            UInt8				direction,
            UInt64				frameNumberStart,
            IOMemoryDescriptor *		pBuffer,
            UInt32				frameCount,
            IOUSBIsocFrame			*pFrames)
{
    IOReturn 				status = kIOReturnSuccess;
    OHCIIsochTransferDescriptorPtr	pTailITD = nil;
    OHCIIsochTransferDescriptorPtr	pNewITD = nil;
    OHCIIsochTransferDescriptorPtr	pTempITD = nil;
    UInt32				i;
    UInt32				curFrameInRequest = 0;
    UInt32				bufferSize = 0;
    UInt32				pageOffset = 0;
    UInt32				curFrameLength;
    UInt32				lastPhysical = 0;
    OHCIEndpointDescriptorPtr		pED;
    UInt32				curFrameInTD = 0;
    UInt16				frameNumber = (UInt16) frameNumberStart;
    UInt64				curFrameNumber = GetFrameNumber();
    UInt64				frameDiff;
    UInt64				maxOffset = (UInt64)(0x00007FF0);
    UInt32				diff32;

    UInt32				itdFlags = 0;
    UInt32				numSegs = 0;
    UInt32				physPageStart = 0;
    UInt32				physPageEnd = 0;
    UInt32				pageSelectMask = 0;
    bool				needNewITD;
    IOPhysicalSegment			segs[2];
    UInt32				tdType;
    IOByteCount				transferOffset;
#if 0
    AbsoluteTime						startTime = UpTime();
    Duration							elapsedTime;
#endif

    if ((frameCount == 0) || (frameCount > 1000))
    {
        USBExpertStatusLevel (3, 123456789, kP_UIMName"Isoch transfer- bad frameCount", kIOReturnBadArgument);
        return kIOReturnBadArgument;
    }

    if (direction == kUSBOut) {
        direction = kOHCIEDDirectionOut;
        tdType = kOHCIIsochronousOutType;
    }
    else if (direction == kUSBIn) {
        direction = kOHCIEDDirectionIn;
        tdType = kOHCIIsochronousInType;
    }
    else
        return kIOReturnInternalError;

    pED = FindIsochronousEndpoint(functionAddress, endpointNumber, direction, nil);

    if (!pED)
    {
        USBExpertStatusLevel (3, 123456789, kP_UIMName"Isoch transfer- endpoint not found, err:", kIOUSBEndpointNotFound);
        return kIOUSBEndpointNotFound;
    }

    if (frameNumberStart <= curFrameNumber)
    {
        if (frameNumberStart < (curFrameNumber - maxOffset))
        {
            USBExpertStatusLevel (3, 123456789, kP_UIMName"Isoch transfer- frameNumberStart:", (UInt32)frameNumberStart);
            USBExpertStatusLevel (3, 123456789, kP_UIMName"Isoch transfer- curFrameNumber:", (UInt32)curFrameNumber);
            USBExpertStatusLevel (3, 123456789, kP_UIMName"Isoch transfer- request frame WAY too old, err:", kIOReturnIsoTooOld);
            return kIOReturnIsoTooOld;
        }
        USBExpertStatusLevel (3, 123456789, kP_UIMName"Isoch transfer- WARNING! curframe later than requested, expect some notSent errors!", 0);
        USBExpertStatusLevel (3, 123456789, kP_UIMName"Isoch transfer- frameNumberStart:", (UInt32)frameNumberStart);
        USBExpertStatusLevel (3, 123456789, kP_UIMName"Isoch transfer- curFrameNumber:", (UInt32)curFrameNumber);
        USBExpertStatusLevel (3, 123456789, kP_UIMName"USBIsocFrame Ptr:", (UInt32)pFrames);
        USBExpertStatusLevel (3, 123456789, kP_UIMName"Isoch transfer- First ITD:", (UInt32)pED->pLogicalTailP);
    } else {					// frameNumberStart > curFrameNumber
        if (frameNumberStart > (curFrameNumber + maxOffset))
        {
            USBExpertStatusLevel (3, 123456789, kP_UIMName"Isoch transfer- frameNumberStart:", (UInt32)frameNumberStart);
            USBExpertStatusLevel (3, 123456789, kP_UIMName"Isoch transfer- curFrameNumber:", (UInt32)curFrameNumber);
            USBExpertStatusLevel (3, 123456789, kP_UIMName"Isoch transfer- request frame too far ahead, err:", kIOReturnIsoTooNew);
            return kIOReturnIsoTooNew;
        }
        frameDiff = frameNumberStart - curFrameNumber;
        diff32 = (UInt32)frameDiff;
        if (diff32 < 2)
        {
            USBExpertStatusLevel (5, 123456789, kP_UIMName"Isoch transfer- WARNING! - frameNumberStart less than 2 ms.", diff32);
            USBExpertStatusLevel (5, 123456789, kP_UIMName"Isoch transfer- frameNumberStart:", (UInt32)frameNumberStart);
            USBExpertStatusLevel (5, 123456789, kP_UIMName"Isoch transfer- curFrameNumber:", (UInt32)curFrameNumber);
        }
    }

    //
    //  Get the total size of buffer
    //
    for ( i = 0; i< frameCount; i++)
    {
        if (pFrames[i].frReqCount > kUSBMaxIsocFrameReqCount)
        {
            USBExpertStatusLevel (3, 123456789, kP_UIMName"Isoch frame too big:", pFrames[i].frReqCount);
            return kIOReturnBadArgument;
        }
        bufferSize += pFrames[i].frReqCount;
    }

    if (direction == kOHCIEDDirectionIn)
        USBExpertStatusLevel (5, 123456789, kP_UIMName"Isoch transfer in, buffer:",
                              (UInt32)pBuffer);
    else
        USBExpertStatusLevel (5, 123456789, kP_UIMName"Isoch transfer out, buffer:",
                              (UInt32)pBuffer);
    USBExpertStatusLevel (5, 123456789, kP_UIMName"Isoch transfer total length:",
                          bufferSize);

    //
    // go ahead and make sure we can grab at least ONE TD, before we lock the buffer	
    //
    pNewITD = OHCIUIMAllocateITD();
    if (pNewITD == nil)
    {
        return kIOReturnNoMemory;
    }

    if (!bufferSize) {
	// Set up suitable dummy info
        numSegs = 1;
        segs[0].location = segs[0].length = 0;
	pageOffset = 0;
    }
    pTailITD = (OHCIIsochTransferDescriptorPtr)pED->pLogicalTailP;	// start with the unused TD on the tail of the list
    OSWriteLittleInt32(&pTailITD->nextTD, 0, pNewITD->pPhysical);	// link in the new ITD
    pTailITD->pLogicalNext = pNewITD;

    needNewITD = false;
    transferOffset = 0;
    while (curFrameInRequest < frameCount) {
        // Get physical segments for next frame
        if(!needNewITD && bufferSize) {
            numSegs = _isoCursor->getPhysicalSegments(pBuffer, transferOffset,
					segs, 2, pFrames[curFrameInRequest].frReqCount);
#if 0
            IOLog("F%ld (%d): numSegs = %ld, (0x%lx, 0x%lx), (0x%lx, 0x%lx)\n", curFrameInRequest,
                pFrames[curFrameInRequest].frReqCount, numSegs, segs[0].location, segs[0].length,
                    segs[1].location, segs[1].length);
#endif
            pageOffset = segs[0].location & kOHCIPageOffsetMask;
            transferOffset += segs[0].length;
            if(numSegs == 2)
                transferOffset += segs[1].length;
        }

        if (curFrameInTD == 0) {
            // set up counters which get reinitialized with each TD
            physPageStart = segs[0].location & kOHCIPageMask;	// for calculating real 13 bit offsets
            pageSelectMask = 0;					// First frame always starts on first page
            needNewITD = false;

            // set up the header of the TD - itdFlags will be stored into flags later
            itdFlags = (UInt16)(curFrameInRequest + frameNumber);
            pTailITD->pIsocFrame = pFrames;		// so we can get back to our info later
            pTailITD->frameNum = curFrameInRequest;	// our own index into the above array
            pTailITD->pType = tdType;			// So interrupt handler knows TD type.
            OSWriteLittleInt32(&pTailITD->bufferPage0, 0,  physPageStart);
        }
        else if ((segs[0].location & kOHCIPageMask) != physPageStart) {
            // pageSelectMask is set if we've already used our one allowed page cross.
            if(pageSelectMask && 
		(((segs[0].location & kOHCIPageMask) != physPageEnd) || numSegs == 2)) {
                // Need new ITD for this
                needNewITD = true;
                continue;
            }
            pageSelectMask = kOHCIPageSize;	// ie. set bit 13
            physPageEnd = segs[numSegs-1].location & kOHCIPageMask;
        }
        curFrameLength = pFrames[curFrameInRequest].frReqCount;
        if ((curFrameInTD > 7) || needNewITD) {
            // we need to start a new TD
            needNewITD = true;	// To simplify test at top of loop.
            itdFlags |= (curFrameInTD-1) << kOHCIITDControl_FCPhase;
            OSWriteLittleInt32(&pTailITD->bufferEnd, 0, lastPhysical);
            curFrameInTD = 0;
            pNewITD = OHCIUIMAllocateITD();
            if (pNewITD == nil) {
                status = kIOReturnNoMemory;
		break;
            }
            // Handy for debugging transfer lists
            itdFlags |= (kOHCIGTDConditionNotAccessed << kOHCIGTDControl_CCPhase);
            OSWriteLittleInt32(&pTailITD->flags, 0, itdFlags);
            pTailITD->completion.action = NULL;
            pTailITD = pTailITD->pLogicalNext;		// this is the "old" pNewTD
            OSWriteLittleInt32(&pTailITD->nextTD, 0, pNewITD->pPhysical);	// link to the "new" pNewTD
            pTailITD->pLogicalNext = pNewITD;
            continue;		// start over
        }
        //
        // at this point we know we have a frame which will fit into the current TD
        //
        // calculate the buffer offset for the beginning of this frame
        OSWriteLittleInt16(&pTailITD->offset[curFrameInTD], 0,
            pageOffset |		// offset
            pageSelectMask |		// offset from BP0 or BufferEnd
            (kOHCIITDOffsetConditionNotAccessed << kOHCIITDOffset_CCPhase));	// mark as unused

        // adjust counters and calculate the physical offset of the end of the frame for the next time around the loop
        curFrameInRequest++;
        curFrameInTD++;
        lastPhysical = segs[numSegs-1].location + segs[numSegs-1].length - 1;
    }			

    if (status != kIOReturnSuccess)
    {
        // unlink the TDs, unlock the buffer, and return the status
        pNewITD = pTailITD->pLogicalNext;	// point to the "old" pNewTD, which will also get deallocated
        pTempITD = (OHCIIsochTransferDescriptorPtr)pED->pLogicalTailP;
        pTailITD = pTempITD->pLogicalNext;	// don't deallocate the real tail!
        pTempITD->pLogicalNext = nil;		// just to make sure
        pTempITD->nextTD = nil;			// just to make sure
        while (pTailITD != pNewITD)
        {
            pTempITD = pTailITD;
            pTailITD = pTailITD->pLogicalNext;
            OHCIUIMDeallocateITD(pTempITD);
        }
    }
    else
    {
        // we have good status, so let's kick off the machine
        // we need to tidy up the last TD, which is not yet complete
        itdFlags |= (curFrameInTD-1) << kOHCIITDControl_FCPhase;
        OSWriteLittleInt32(&pTailITD->flags, 0, itdFlags);
        OSWriteLittleInt32(&pTailITD->bufferEnd, 0, lastPhysical);
        pTailITD->completion = completion;
        //print_itd(pTailITD);
        // Make new descriptor the tail
        pED->pLogicalTailP = pNewITD;
        OSWriteLittleInt32(&pED->tdQueueTailPtr, 0, pNewITD->pPhysical);
    }

#if 0
    elapsedTime = AbsoluteDeltaToDuration(UpTime(), startTime);
    if ((elapsedTime > 0) || (elapsedTime < -700))	// measured in milliseconds or more than 700 microseconds to queue everything
    {
        USBExpertStatusLevel (4, 123456789, kP_UIMName"Isoch WARNING! > 1 ms (or getting close): ", elapsedTime);
        USBExpertStatusLevel (4, 123456789, kP_UIMName"Isoch transfer frame count: ", frameCount);
        USBExpertStatusLevel (4, 123456789, kP_UIMName"Isoch transfer buffer size: ", bufferSize);
    }
#endif
    //print_isoc_ed(pED);

    return status;

}

IOReturn AppleOHCI::UIMAbortEndpoint(
            short				functionAddress,
            short				endpointNumber,
            short				direction)
{
    OHCIRegistersPtr		pOHCIRegisters;
    OHCIEndpointDescriptorPtr	pED;
    OHCIEndpointDescriptorPtr	pEDQueueBack;
    UInt32			something, controlMask;

    if (functionAddress == 0)
    {
        IOLog("UIMAbortEndpoint: Error: operation on root hub\n");
        return(kIOReturnSuccess);
    }

    pOHCIRegisters = pOHCIUIMData->pOHCIRegisters;

    if (direction == kUSBOut)
        direction = kOHCIEDDirectionOut;
    else if (direction == kUSBIn)
        direction = kOHCIEDDirectionIn;
    else
        direction = kOHCIEDDirectionTD;
    
    //search for endpoint descriptor
    pED = FindEndpoint (functionAddress,
                        endpointNumber,
                        direction,
                        &pEDQueueBack,
                        &controlMask);
    if (!pED)
    {
        IOLog("UIMAbortEndpoint: Error: Could not find endpoint\n");
        return (kIOUSBEndpointNotFound);
    }

    pED->flags |= OSSwapInt32(kOHCIEDControl_K);	// mark the ED as skipped

    // poll for interrupt  zzzzz turn into real interrupt
    pOHCIRegisters->hcInterruptStatus = OSSwapInt32(kOHCIHcInterrupt_SF);
    IOSleep(1);
    something = OSSwapInt32(pOHCIRegisters->hcInterruptStatus)
        					& kOHCIInterruptSOFMask;

    if (!something)
    {
        /* This should have been set, just in case wait another ms */
        IOSleep(1);
    }

    RemoveTDs(pED);

    pED->flags &= ~OSSwapInt32(kOHCIEDControl_K);	// activate ED again


    return (kIOReturnSuccess);
}

IOReturn AppleOHCI::UIMDeleteEndpoint(
            short				functionAddress,
            short				endpointNumber,
            short				direction)
{
    OHCIRegistersPtr		pOHCIRegisters;
    OHCIEndpointDescriptorPtr	pED;
    OHCIEndpointDescriptorPtr	pEDQueueBack;
    UInt32			hcControl;
    UInt32			something, controlMask;
    //	UInt32			edDirection;


#if 0
    if (functionAddress == 0)
    {
        IOLog("UIMDeleteEndpoint: Error: operation on root hub\n");
        return(kIOReturnSuccess);
    }
#endif

    pOHCIRegisters = pOHCIUIMData->pOHCIRegisters;

    if (direction == kUSBOut)
        direction = kOHCIEDDirectionOut;
    else if (direction == kUSBIn)
        direction = kOHCIEDDirectionIn;
    else
        direction = kOHCIEDDirectionTD;

    //search for endpoint descriptor
    pED = FindEndpoint (functionAddress,
                        endpointNumber,
                        direction,
                        &pEDQueueBack,
                        &controlMask);
    if (!pED)
    {
        DEBUGLOG("UIMDeleteEndpoint: Error: Could not find endpoint\n");
        return (kIOUSBEndpointNotFound);
    }
    
    // Remove Endpoint
    //mark sKipped
    pED->flags |= OSSwapInt32(kOHCIEDControl_K);
    //	edDirection = OSSwapInt32(pED->dWord0) & kOHCIEndpointDirectionMask;
    // remove pointer wraps
    pEDQueueBack->nextED = pED->nextED;
    pEDQueueBack->pLogicalNext = pED->pLogicalNext;

    // clear some bit in hcControl
    hcControl = OSSwapInt32(pOHCIRegisters->hcControl);	
    hcControl &= ~controlMask;
    hcControl &= OHCIBitRange(0, 10);

    pOHCIRegisters->hcControl = OSSwapInt32(hcControl);

    // poll for interrupt  zzzzz turn into real interrupt
    pOHCIRegisters->hcInterruptStatus = OSSwapInt32(kOHCIHcInterrupt_SF);
    IOSleep(1);
    something = OSSwapInt32(pOHCIRegisters->hcInterruptStatus)
        					& kOHCIInterruptSOFMask;
    if (!something)
    {
        /* This should have been set, just in case wait another ms */
        IOSleep(1);
    }
    // restart hcControl
    hcControl |= controlMask;
    pOHCIRegisters->hcControl = OSSwapInt32(hcControl);

#if (DEBUGGING_LEVEL > 0)
    DEBUGLOG("UIMDeleteEndpoint: pED=%lx hcControl=%lx SOF int?=%s\n",
          (UInt32)pED, hcControl, something?"yes":"no");
#endif
    if (GetEDType(pED) == kOHCIEDFormatIsochronousTD)
    {
        UInt32 maxPacketSize = (OSReadLittleInt32(&pED->flags, 0) & kOHCIEDControl_MPS) >>
                                                                            kOHCIEDControl_MPSPhase;
        pOHCIUIMData->isochBandwidthAvail += maxPacketSize;
        USBExpertStatusLevel (5, 123456789,
            kP_UIMName"Isoch Endpoint delete- bandwidth returned = ", maxPacketSize);
        USBExpertStatusLevel (5, 123456789,
            kP_UIMName"Isoch Endpoint delete- new available = ", pOHCIUIMData->isochBandwidthAvail);
    }
    RemoveAllTDs(pED);

    pED->nextED = nil;

    //deallocate ED
    OHCIUIMDeallocateED(pED);
#if (DEBUGGING_LEVEL > 2)
        print_bulk_list(pOHCIUIMData);
        print_control_list(pOHCIUIMData);
#endif
    return (kIOReturnSuccess);
}

void AppleOHCI::ReturnTransactions(
            OHCIGeneralTransferDescriptor 	*transaction,
            UInt32				tail)
{
    UInt32                          physicalAddress;
    OHCIGeneralTransferDescriptor   *nextTransaction;

    while(transaction->pPhysical != tail)
    {
        if (transaction->completion.action != nil) {
            IOUSBCompletion completion;
            // zero out callback first then call it
            completion = transaction->completion;
            transaction->completion.action = nil;
            complete(completion, kIOUSBPipeStalled, 0);
        }
        /* walk the physically-addressed list */
        physicalAddress =
        	OSSwapInt32(transaction->nextTD) & kOHCIHeadPMask;
        nextTransaction = (OHCIGeneralTransferDescriptorPtr)
                            OHCIUIMGetLogicalAddress (physicalAddress);
        OHCIUIMDeallocateTD(transaction);
        transaction = nextTransaction;
        if(transaction == nil)
        {
            IOLog("returnTransactions: Return queue broken");
            break;
        }
    }
}

IOReturn AppleOHCI::UIMClearEndpointStall(
            short				functionAddress,
            short				endpointNumber,
            short				direction)
{
    OHCIRegistersPtr			pOHCIRegisters;
    OHCIEndpointDescriptorPtr		pEDQueueBack, pED;
    OHCIGeneralTransferDescriptor	*transaction;
    UInt32				tail, controlMask;


#if DEBUGGING_LEVEL > 0
    DEBUGLOG("%s: clearing endpoint %d:%d stall\n", getName(), functionAddress, endpointNumber);
#endif
    
    if (pOHCIUIMData->rootHubFuncAddress == functionAddress)
    {
        IOLog("UIMDeleteEndpoint: Error: operation on root hub\n");
        return(kIOReturnSuccess);
    }

    if (direction == kUSBOut)
        direction = kOHCIEDDirectionOut;
    else if (direction == kUSBIn)
        direction = kOHCIEDDirectionIn;
    else
        direction = kOHCIEDDirectionTD;

    pOHCIRegisters = pOHCIUIMData->pOHCIRegisters;

    transaction = nil;
    tail = nil;
    //search for endpoint descriptor
    pED = FindEndpoint (functionAddress,
                        endpointNumber,
                        direction,
                        &pEDQueueBack,
                        &controlMask);
    if (!pED)
    {
        IOLog("UIMDeleteEndpoint: Error: Could not find endpoint\n");
        return (kIOUSBEndpointNotFound);
    }

    if (pED != nil)
    {
        tail = OSSwapInt32(pED->tdQueueTailPtr);
        transaction = (OHCIGeneralTransferDescriptor *)
            OHCIUIMGetLogicalAddress(OSSwapInt32(pED->tdQueueHeadPtr) & kOHCIHeadPMask);
        /* unlink all transactions at once (this also clears the halted bit) */
        pED->tdQueueHeadPtr = pED->tdQueueTailPtr;
        pED->pLogicalHeadP = pED->pLogicalTailP;
    }	

    if (transaction != nil)
    {
        ReturnTransactions(transaction, tail);
    }
    return (kIOReturnSuccess);
}

OHCIEndpointDescriptorPtr AppleOHCI::AddEmptyEndPoint(
        UInt8 						functionAddress,
        UInt8						endpointNumber,
        UInt16						maxPacketSize,
        UInt8						speed,
        UInt8						direction,
        OHCIEndpointDescriptorPtr			pED,
        OHCIEDFormat					format)
{
    UInt32				myFunctionAddress,
    					myEndpointNumber,
    					myEndpointDirection,
    					myMaxPacketSize,
    					mySpeed,
    					myFormat;
    OHCIEndpointDescriptorPtr		pOHCIEndpointDescriptor;
    OHCIGeneralTransferDescriptorPtr	pOHCIGeneralTransferDescriptor;
    OHCIIsochTransferDescriptorPtr	pITD;

    
    pOHCIEndpointDescriptor = (OHCIEndpointDescriptorPtr) OHCIUIMAllocateED();
    myFunctionAddress = ((UInt32) functionAddress) << kOHCIEDControl_FAPhase;
    myEndpointNumber = ((UInt32) endpointNumber) << kOHCIEDControl_ENPhase;
    myEndpointDirection = ((UInt32) direction) << kOHCIEDControl_DPhase;
    mySpeed = ((UInt32) speed) << kOHCIEDControl_SPhase;
    myMaxPacketSize = ((UInt32) maxPacketSize) << kOHCIEDControl_MPSPhase;
    myFormat = ((UInt32) format) << kOHCIEDControl_FPhase;
    pOHCIEndpointDescriptor->flags =
        OSSwapInt32(myFunctionAddress
                     | myEndpointNumber
                     | myEndpointDirection
                     | myMaxPacketSize
                     | mySpeed
                     | myFormat);

    if (format == kOHCIEDFormatGeneralTD)
    {
        pOHCIGeneralTransferDescriptor = OHCIUIMAllocateTD();

        /* These were previously nil */
        pOHCIEndpointDescriptor->tdQueueTailPtr =
            OSSwapInt32( pOHCIGeneralTransferDescriptor->pPhysical);
        pOHCIEndpointDescriptor->tdQueueHeadPtr =
            OSSwapInt32( pOHCIGeneralTransferDescriptor->pPhysical);
        pOHCIEndpointDescriptor->pLogicalTailP =
            pOHCIGeneralTransferDescriptor;
        pOHCIEndpointDescriptor->pLogicalHeadP =
            pOHCIGeneralTransferDescriptor;
    }
    else
    {
        pITD = OHCIUIMAllocateITD();

        /* These were previously nil */
        pOHCIEndpointDescriptor->tdQueueTailPtr =
            OSSwapInt32( pITD->pPhysical);
        pOHCIEndpointDescriptor->tdQueueHeadPtr =
            OSSwapInt32( pITD->pPhysical);
        pOHCIEndpointDescriptor->pLogicalTailP = pITD;
        pOHCIEndpointDescriptor->pLogicalHeadP = pITD;		

    }

    pOHCIEndpointDescriptor->nextED = pED->nextED;
    pOHCIEndpointDescriptor->pLogicalNext = pED->pLogicalNext;
    pED->pLogicalNext = pOHCIEndpointDescriptor;
    pED->nextED = OSSwapInt32(pOHCIEndpointDescriptor->pPhysical);

    return (pOHCIEndpointDescriptor);
}

OHCIEndpointDescriptorPtr AppleOHCI::FindControlEndpoint (
	short 						functionNumber, 
	short						endpointNumber, 
	OHCIEndpointDescriptorPtr   			*pEDBack)
{
    UInt32			unique;
    OHCIEndpointDescriptorPtr	pEDQueue;
    OHCIEndpointDescriptorPtr	pEDQueueBack;

	
    // search for endpoint descriptor
    unique = (UInt32) ((((UInt32) endpointNumber) << kOHCIEndpointNumberOffset)
                       		| ((UInt32) functionNumber));
    pEDQueueBack = (OHCIEndpointDescriptorPtr) pOHCIUIMData->pControlHead;
    pEDQueue = (OHCIEndpointDescriptorPtr) pEDQueueBack->pLogicalNext;

    while ((UInt32) pEDQueue != pOHCIUIMData->pControlTail)
    {
        if ((OSSwapInt32(pEDQueue->flags) & kUniqueNumNoDirMask) == unique)
        {
            *pEDBack = pEDQueueBack;
            return (pEDQueue);
        }
        else
        {
            pEDQueueBack = pEDQueue;
            pEDQueue = (OHCIEndpointDescriptorPtr) pEDQueue->pLogicalNext;
        }
    }
    if (pOHCIUIMData->OptiOn)
    {
        pEDQueue = FindBulkEndpoint (functionNumber, endpointNumber,
                                     kOHCIEDDirectionTD, &pEDQueueBack);
        *pEDBack = pEDQueueBack;
        return (pEDQueue);
    }
    return (nil);
}


OHCIEndpointDescriptorPtr AppleOHCI::FindBulkEndpoint (
	short 						functionNumber, 
	short						endpointNumber,
	short						direction,
	OHCIEndpointDescriptorPtr			*pEDBack)
{

    UInt32			unique;
    UInt32			myEndpointDirection;
    OHCIEndpointDescriptorPtr	pEDQueue;
    OHCIEndpointDescriptorPtr	pEDQueueBack;


    // search for endpoint descriptor
    myEndpointDirection = ((UInt32) direction) << kOHCIEndpointDirectionOffset;
    unique = (UInt32) ((((UInt32) endpointNumber) << kOHCIEndpointNumberOffset)
                       | ((UInt32) functionNumber) | myEndpointDirection);
    pEDQueueBack = (OHCIEndpointDescriptorPtr) pOHCIUIMData->pBulkHead;
    pEDQueue = (OHCIEndpointDescriptorPtr) pEDQueueBack->pLogicalNext;

    while (((UInt32) pEDQueue) != pOHCIUIMData->pBulkTail )
    {
        if ((OSSwapInt32(pEDQueue->flags) & kUniqueNumMask) == unique)
        {
            *pEDBack = pEDQueueBack;
            return (pEDQueue);
        }
        else
        {
            pEDQueueBack = pEDQueue;
            pEDQueue = (OHCIEndpointDescriptorPtr) pEDQueue->pLogicalNext;
        }
    }
    return (nil);
}


OHCIEndpointDescriptorPtr AppleOHCI::FindEndpoint (
	short 						functionNumber, 
	short 						endpointNumber,
	short 						direction, 
	OHCIEndpointDescriptorPtr 			*pEDQueueBack, 
	UInt32 						*controlMask)
{
    OHCIEndpointDescriptorPtr pED, pEDBack;

    pED = FindControlEndpoint (functionNumber, endpointNumber, &pEDBack);
    if (pED != nil)
    {
        *pEDQueueBack = pEDBack;
        *controlMask = kOHCIHcControl_CLE;
        return (pED);
    }

    pED = FindBulkEndpoint(functionNumber, endpointNumber, direction, &pEDBack);
    if (pED != nil)
    {
        *pEDQueueBack = pEDBack;

        *controlMask = kOHCIHcControl_BLE;
        //zzzz Opti Bug
        if(pOHCIUIMData->OptiOn)
            *controlMask = kOHCIHcControl_CLE;
        return (pED);
    }

    pED = FindInterruptEndpoint(functionNumber, endpointNumber, direction,
	&pEDBack);
    if (pED != nil)
    {
        *pEDQueueBack = pEDBack;
        *controlMask = 0;
        return (pED);	
    }

    pED = FindIsochronousEndpoint(functionNumber, endpointNumber,
                                  direction, &pEDBack);
    *pEDQueueBack = pEDBack;
    *controlMask = 0;
    return (pED);	
}


OHCIEndpointDescriptorPtr AppleOHCI::FindIsochronousEndpoint(
	short 						functionNumber,
	short						endpointNumber,
	short 						direction, 
	OHCIEndpointDescriptorPtr			*pEDBack)
{
    UInt32			myEndpointDirection;
    UInt32			unique;
    OHCIEndpointDescriptorPtr	pEDQueue, pEDQueueBack;

    // search for endpoint descriptor
    myEndpointDirection = ((UInt32) direction) << kOHCIEndpointDirectionOffset;
    unique = (UInt32) ((((UInt32) endpointNumber) << kOHCIEndpointNumberOffset)
                       | ((UInt32) functionNumber) | myEndpointDirection);

    pEDQueueBack = (OHCIEndpointDescriptorPtr) pOHCIUIMData->pIsochHead;
    pEDQueue = (OHCIEndpointDescriptorPtr) pEDQueueBack->pLogicalNext;
    while (((UInt32) pEDQueue) != pOHCIUIMData->pIsochTail )
    {
        if ((OSSwapInt32(pEDQueue->flags) & kUniqueNumMask) == unique)
        {
            if(pEDBack)
                *pEDBack = pEDQueueBack;
            return (pEDQueue);
        }
        else
        {
            pEDQueueBack = pEDQueue;
            pEDQueue = (OHCIEndpointDescriptorPtr) pEDQueue->pLogicalNext;
        }
    }
    return (nil);
}


OHCIEndpointDescriptorPtr AppleOHCI::FindInterruptEndpoint(
	short 					functionNumber,
	short					endpointNumber,
        short					direction,
	OHCIEndpointDescriptorPtr		*pEDBack)
{
    OHCIRegistersPtr			pOHCIRegisters;
    UInt32				myEndpointDirection;
    UInt32				unique;
    OHCIEndpointDescriptorPtr		pEDQueue;
    OHCIIntHeadPtr			pInterruptHead;
    int					i;
    UInt32				temp;
    
    pOHCIRegisters = pOHCIUIMData->pOHCIRegisters;
    pInterruptHead = pOHCIUIMData->pInterruptHead;

    //search for endpoint descriptor
    myEndpointDirection = ((UInt32) direction) << kOHCIEndpointDirectionOffset;
    unique = (UInt32) ((((UInt32) endpointNumber) << kOHCIEDControl_ENPhase)
                       | (((UInt32) functionNumber) << kOHCIEDControl_FAPhase)
                       | myEndpointDirection);

    for (i = 0; i < 63; i++)
    {
        pEDQueue = pInterruptHead[i].pHead;
        *pEDBack = pEDQueue;
        // BT do this first, or you find the dummy endpoint
        // all this is hanging off. It matches 0,0
        pEDQueue = (OHCIEndpointDescriptorPtr) pEDQueue->pLogicalNext;
        while (pEDQueue != pInterruptHead[i].pTail)
        {
            temp = (OSSwapInt32(pEDQueue->flags)) & kUniqueNumMask;

            if ( temp == unique)
            {
                return(pEDQueue);
            }
            *pEDBack = pEDQueue;
            pEDQueue = (OHCIEndpointDescriptorPtr) pEDQueue->pLogicalNext;
        }
    }
    return(nil);
}

bool AppleOHCI::DetermineInterruptOffset(
    UInt32          pollingRate,
    UInt32          /* reserveBandwidth */,
    int             *offset)
{
    int num;
    OHCIRegistersPtr            pOHCIRegisters;

    pOHCIRegisters = pOHCIUIMData->pOHCIRegisters;

    num = OSSwapInt32(pOHCIRegisters->hcFmNumber)&kOHCIFmNumberMask;
    if (pollingRate <  1)
        //error condition
        return(false);
    else if(pollingRate < 2)
        *offset = 62;
    else if(pollingRate < 4)
        *offset = (num%2) + 60;
    else if(pollingRate < 8)
        *offset = (num%4) + 56;
    else if(pollingRate < 16)
        *offset = (num%8) + 48;
    else if(pollingRate < 32)
        *offset = (num%16) + 32;
    else
        *offset = (num%32) + 0;
    return (true);
}

static char *cc_errors[] = {
    "NO ERROR",			/* 0 */
    "CRC",			/* 0 */
    "BIT STUFFING",		/* 0 */
    "DATA TOGGLE MISMATCH",	/* 0 */
    "STALL",			/* 0 */
    "DEVICE NOT RESPONDING",	/* 0 */
    "PID CHECK FAILURE",	/* 0 */
    "UNEXPECTED PID",		/* 0 */
    "DATA OVERRUN",		/* 0 */
    "DATA UNDERRUN",		/* 0 */
    "??",			/* reserved */
    "??",			/* reserved */
    "BUFFER OVERRUN",		/* 0 */
    "BUFFER UNDERRUN",		/* 0 */
    "NOT ACCESSED A",		/* not processed yet */
    "NOT ACCESSED B"		/* not processed yet */
};

void print_td(OHCIGeneralTransferDescriptorPtr pTD)
{
    UInt32 w0, dir, err;

    if (pTD == 0) return;

    w0 = OSSwapInt32(pTD->flags);
    dir = (w0 & kOHCIGTDControl_DP) >> kOHCIGTDControl_DPPhase;
    err = (w0 & kOHCIGTDControl_CC) >> kOHCIGTDControl_CCPhase;
    DEBUGLOG("\tTD(0x%08lx->0x%08lx) dir=%s cc=%s errc=%ld t=%ld rd=%s: c=0x%08lx cbp=0x%08lx, next=0x%08lx, bend=0x%08lx\n",
	(UInt32)pTD, pTD->pPhysical,
	dir == 0 ? "SETUP" : (dir==2?"IN":"OUT"),
	cc_errors[err],
	(w0 & kOHCIGTDControl_EC) >> kOHCIGTDControl_ECPhase,
	(w0 & kOHCIGTDControl_T)  >> kOHCIGTDControl_TPhase,
	(w0 & kOHCIGTDControl_R)?"yes":"no",
	OSSwapInt32(pTD->flags),
	OSSwapInt32(pTD->currentBufferPtr),
	OSSwapInt32(pTD->nextTD),
	OSSwapInt32(pTD->bufferEnd));
}

void print_itd(OHCIIsochTransferDescriptorPtr pTD)
{
    UInt32 w0, err;
    int i;
    if (pTD == 0) return;

    w0 = OSReadLittleInt32(&pTD->flags, 0);
    err = (w0 & kOHCIITDControl_CC) >> kOHCIITDControl_CCPhase;
    DEBUGLOG("\tTD(0x%08lx->0x%08lx) cc=%s fc=%ld sf=0x%lx c=0x%08lx bp0=0x%08lx, next=0x%08lx, bend=0x%08lx\n",
        (UInt32)pTD, pTD->pPhysical,
        cc_errors[err],
        (w0 & kOHCIITDControl_FC) >> kOHCIITDControl_FCPhase,
        (w0 & kOHCIITDControl_SF)  >> kOHCIITDControl_SFPhase,
        w0,
        OSSwapInt32(pTD->bufferPage0),
        OSSwapInt32(pTD->nextTD),
        OSSwapInt32(pTD->bufferEnd));
    for(i=0; i<8; i++) {
	DEBUGLOG("Offset/PSW %d = 0x%x\n", i, OSReadLittleInt16(&pTD->offset[i], 0));
    }
    DEBUGLOG("frames = 0x%lx, FrameNumber %ld\n", (UInt32)pTD->pIsocFrame, pTD->frameNum);
}

void print_ed(OHCIEndpointDescriptorPtr pED)
{
    OHCIGeneralTransferDescriptorPtr	pTD;
    UInt32 w0;


    if (pED == 0) {
	kprintf("Null ED\n");
	return;
    }
    w0 = OSSwapInt32(pED->flags);

    if ((w0 & kOHCIEDControl_K) == 0 /*noskip*/)
    {
        DEBUGLOG("ED(0x%08lx->0x%08lx) %ld:%ld d=%ld s=%s sk=%s i=%s max=%ld : c=0x%08lx tail=0x%08lx, head=0x%08lx, next=0x%08lx\n",
              (UInt32)pED, (UInt32)pED->pPhysical,
              (w0 & kOHCIEDControl_FA) >> kOHCIEDControl_FAPhase,
              (w0 & kOHCIEDControl_EN) >> kOHCIEDControl_ENPhase,
              (w0 & kOHCIEDControl_D)  >> kOHCIEDControl_DPhase,
              w0 & kOHCIEDControl_S?"low":"hi",
              w0 & kOHCIEDControl_K?"yes":"no",
              w0 & kOHCIEDControl_F?"yes":"no",
              (w0 & kOHCIEDControl_MPS) >> kOHCIEDControl_MPSPhase,
              OSSwapInt32(pED->flags),
              OSSwapInt32(pED->tdQueueTailPtr),
              OSSwapInt32(pED->tdQueueHeadPtr),
              OSSwapInt32(pED->nextED));

        //pTD = (OHCIGeneralTransferDescriptorPtr) pED->pVirtualHeadP;
       pTD = (OHCIGeneralTransferDescriptorPtr)
           (OSSwapInt32(pED->tdQueueHeadPtr) & kOHCINextEndpointDescriptor_nextED);
        
        while (pTD != 0)
        {
            DEBUGLOG("\t");
            print_td(pTD);
            pTD = pTD->pLogicalNext;
        }
    }
}

void print_isoc_ed(OHCIEndpointDescriptorPtr pED)
{
    OHCIIsochTransferDescriptorPtr	pTD;
    UInt32 w0;


    if (pED == 0) {
        kprintf("Null ED\n");
        return;
    }
    w0 = OSSwapInt32(pED->flags);

    if ((w0 & kOHCIEDControl_K) == 0 /*noskip*/)
    {
        DEBUGLOG("ED(0x%08lx->0x%08lx) %ld:%ld d=%ld s=%s sk=%s i=%s max=%ld : c=0x%08lx tail=0x%08lx, head=0x%08lx, next=0x%08lx\n",
              (UInt32)pED, (UInt32)pED->pPhysical,
              (w0 & kOHCIEDControl_FA) >> kOHCIEDControl_FAPhase,
              (w0 & kOHCIEDControl_EN) >> kOHCIEDControl_ENPhase,
              (w0 & kOHCIEDControl_D)  >> kOHCIEDControl_DPhase,
              w0 & kOHCIEDControl_S?"low":"hi",
              w0 & kOHCIEDControl_K?"yes":"no",
              w0 & kOHCIEDControl_F?"yes":"no",
              (w0 & kOHCIEDControl_MPS) >> kOHCIEDControl_MPSPhase,
              OSSwapInt32(pED->flags),
              OSSwapInt32(pED->tdQueueTailPtr),
              OSSwapInt32(pED->tdQueueHeadPtr),
              OSSwapInt32(pED->nextED));

        pTD = (OHCIIsochTransferDescriptorPtr) pED->pLogicalHeadP;
        while (pTD != 0)
        {
            DEBUGLOG("\t");
            print_itd(pTD);
            pTD = pTD->pLogicalNext;
        }
    }
}

void print_list(OHCIEndpointDescriptorPtr pListHead,
                OHCIEndpointDescriptorPtr pListTail)
{
    OHCIEndpointDescriptorPtr		pED, pEDTail;


    pED = (OHCIEndpointDescriptorPtr) pListHead;
    pEDTail = (OHCIEndpointDescriptorPtr) pListTail;

    while (pED != pEDTail)
    {
        print_ed(pED);
        pED = (OHCIEndpointDescriptorPtr) pED->pLogicalNext;
    }
    print_ed(pEDTail);
}

void print_control_list(OHCIUIMDataPtr pData)
{
    DEBUGLOG("Control List: h/w head = 0x%lx\n",
	OSReadLittleInt32(&pData->pOHCIRegisters->hcControlHeadED, 0));
    print_list((OHCIEndpointDescriptorPtr) pData->pControlHead,
               (OHCIEndpointDescriptorPtr) pData->pControlTail);
}

void print_bulk_list(OHCIUIMDataPtr pData)
{
    DEBUGLOG("Bulk List:\n");
    DEBUGLOG("Bulk List: h/w head = 0x%lx\n",
	OSReadLittleInt32(&pData->pOHCIRegisters->hcBulkHeadED, 0));
    print_list((OHCIEndpointDescriptorPtr) pData->pBulkHead,
               (OHCIEndpointDescriptorPtr) pData->pBulkTail);
}

void print_int_list(OHCIUIMDataPtr pData)
{
    OHCIIntHeadPtr	pInterruptHead = pData->pInterruptHead;
    int			i;
    UInt32		w0;
    OHCIEndpointDescriptorPtr pED;


    DEBUGLOG("Interrupt List:\n");
    for (i = 0; i < 63; i++)
    {
        pED = (OHCIEndpointDescriptorPtr) pInterruptHead[i].pHead->pLogicalNext;
        w0 = OSSwapInt32(pED->flags);

        if ((w0 & kOHCIEDControl_K) == 0 /*noskip*/)
        {
            DEBUGLOG("%d:", i);
            print_ed(pED);
        }
    }
}