#include <libkern/OSByteOrder.h>
#include <IOKit/IOMemoryCursor.h>
#include <IOKit/IOMessage.h>
#include <IOKit/IOKitKeys.h>
#include <IOKit/IOFilterInterruptEventSource.h>
#include <IOKit/usb/USB.h>
#include <IOKit/usb/IOUSBLog.h>
#include <IOKit/pccard/IOPCCard.h>
#include <IOKit/usb/IOUSBRootHubDevice.h>
#include "AppleUSBEHCI.h"
#include "AppleEHCIedMemoryBlock.h"
#include "AppleEHCItdMemoryBlock.h"
#include "AppleEHCIitdMemoryBlock.h"
#include "AppleEHCIsitdMemoryBlock.h"
#define super IOUSBControllerV2
#define self this
#define NUM_BUFFER_PAGES 9 // 54
#define NUM_TDS 255 // 1500
#define NUM_EDS 256 // 1500
#define NUM_ITDS 192 // 1300
OSDefineMetaClassAndStructors(AppleUSBEHCI, IOUSBControllerV2)
bool
AppleUSBEHCI::init(OSDictionary * propTable)
{
if (!super::init(propTable))
return false;
_ehciBusState = kEHCIBusStateOff;
_ehciAvailable = true;
_hasPCIPwrMgmt = false;
_intLock = IOLockAlloc();
if (!_intLock)
goto ErrorExit;
_controllerSpeed = kUSBDeviceSpeedHigh; _wdhLock = IOSimpleLockAlloc();
if (!_wdhLock)
goto ErrorExit;
_isochScheduleLock = IOSimpleLockAlloc();
if (!_isochScheduleLock)
goto ErrorExit;
_uimInitialized = false;
_producerCount = 1;
_consumerCount = 1;
_portDetectInterruptThread = thread_call_allocate((thread_call_func_t)PortDetectInterruptThreadEntry, (thread_call_param_t)this);
if ( !_portDetectInterruptThread)
goto ErrorExit;
return true;
ErrorExit:
if (_intLock)
IOLockFree(_intLock);
if ( _wdhLock )
IOSimpleLockFree(_wdhLock);
if (_isochScheduleLock)
IOSimpleLockFree(_isochScheduleLock);
return false;
}
void
AppleUSBEHCI::free()
{
IOLockFree( _intLock );
IOSimpleLockFree( _wdhLock );
IOSimpleLockFree( _isochScheduleLock );
if (_portDetectInterruptThread)
{
thread_call_cancel(_portDetectInterruptThread);
thread_call_free(_portDetectInterruptThread);
}
super::free();
}
void AppleUSBEHCI::showRegisters(char *s)
{
int i, numPorts;
USBLog(3,"EHCIUIM -- showRegisters %s version: 0x%x", s, USBToHostWord(_pEHCICapRegisters->HCIVersion));
USBLog(3,"USBCMD: %p", (void*)USBToHostLong(_pEHCIRegisters->USBCMD));
USBLog(3,"USBSTS: %p", (void*)USBToHostLong(_pEHCIRegisters->USBSTS));
USBLog(3,"USBIntr: %p", (void*)USBToHostLong(_pEHCIRegisters->USBIntr));
USBLog(3,"FRIndex: %p", (void*)USBToHostLong(_pEHCIRegisters->FRIndex));
USBLog(3,"CTRLDSSeg: %p", (void*)USBToHostLong(_pEHCIRegisters->CTRLDSSegment));
USBLog(3,"PerListBase: %p", (void*)USBToHostLong(_pEHCIRegisters->PeriodicListBase));
USBLog(3,"AsyncListAd: %p", (void*)USBToHostLong(_pEHCIRegisters->AsyncListAddr));
USBLog(3,"ConfFlg: %p", (void*)USBToHostLong(_pEHCIRegisters->ConfigFlag));
numPorts = USBToHostLong(_pEHCICapRegisters->HCSParams) & kEHCINumPortsMask;
for(i=0;i<numPorts;i++)
{
UInt32 x;
x = USBToHostLong(_pEHCIRegisters->PortSC[i]);
if(x != 0x1000)
USBLog(3,"PortSC[%d]: 0x%lx", i+1, x);
}
}
bool
AppleUSBEHCI::start( IOService * provider )
{
USBLog(7, "AppleUSBEHCI[%p]::start", this);
if( !super::start(provider))
return (false);
initForPM(_device);
USBLog(7, "AppleUSBEHCI[%p]::start", this);
return true;
}
IOReturn
AppleUSBEHCI::UIMInitialize(IOService * provider)
{
UInt32 CapLength, USBCmd, hccparams, ist;
IOReturn err = kIOReturnSuccess;
UInt16 lvalue;
UInt8 bValue;
int i;
USBLog(7, "AppleUSBEHCI[%p]::UIMInitialize", this);
_device = OSDynamicCast(IOPCIDevice, provider);
if(_device == NULL)
return kIOReturnBadArgument;
do {
if (!(_deviceBase = provider->mapDeviceMemoryWithIndex(0)))
{
USBError(1, "AppleUSBEHCI[%p]::UIMInitialize - unable to get device memory", this);
err = kIOReturnNoResources;
break;
}
USBLog(3, "AppleUSBEHCI[%p]::UIMInitialize config @ %lx (%lx)\n", this,
(long)_deviceBase->getVirtualAddress(),
_deviceBase->getPhysicalAddress());
SetVendorInfo();
_filterInterruptSource = IOFilterInterruptEventSource::filterInterruptEventSource(this,
AppleUSBEHCI::InterruptHandler,
AppleUSBEHCI::PrimaryInterruptFilter,
_device );
if ( !_filterInterruptSource )
{
USBError(1,"AppleUSBEHCI[%p]: unable to get filterInterruptEventSource", this);
err = kIOReturnNoResources;
break;
}
err = _workLoop->addEventSource(_filterInterruptSource);
if ( err != kIOReturnSuccess )
{
USBError(1,"AppleUSBEHCI[%p]: unable to add filter event source: 0x%x", this, err);
err = kIOReturnNoResources;
break;
}
_errataBits = GetErrataBits(_vendorID, _deviceID, _revisionID);
USBLog(7, "AppleUSBEHCI[%p]::UIMInitialize - errata bits=%p", this, (void*)_errataBits);
_pEHCICapRegisters = (EHCICapRegistersPtr) _deviceBase->getVirtualAddress();
lvalue = _device->configRead16(cwCommand);
_device->configWrite16(cwCommand, lvalue | cwCommandEnableMemorySpace);
err = AcquireOSOwnership();
if ( err != kIOReturnSuccess )
{
USBError(1,"AppleUSBEHCI[%p]: unable to obtain ownership: 0x%x", this, err);
break;
}
hccparams = USBToHostLong(_pEHCICapRegisters->HCCParams);
if (hccparams & kEHCI64Bit)
_is64bit = true;
else
_is64bit = false;
setProperty("64bit", _is64bit);
ist = (hccparams & kEHCIISTMask) >> kEHCIISTPhase;
if (!ist)
_istKeepAwayFrames = 1;
else
_istKeepAwayFrames = 2;
CapLength = _pEHCICapRegisters->CapLength;
_pEHCIRegisters = (EHCIRegistersPtr) ( ((UInt32)_pEHCICapRegisters) + CapLength);
_workLoop->enableAllInterrupts();
_rootHubFuncAddress = 1;
if (_is64bit)
_pEHCIRegisters->CTRLDSSegment = 0;
_pEHCIRegisters->USBCMD = 0; IOSync();
for (i=0; (i < 100) && !(USBToHostLong(_pEHCIRegisters->USBSTS) & kEHCIHCHaltedBit); i++)
IOSleep(1);
if (i >= 100)
{
USBError(1, "AppleUSBEHCI[%p]::UIMInitialize - could not get chip to halt within 100 ms", this);
err = kIOReturnInternalError;
break;
}
_pEHCIRegisters->USBCMD = HostToUSBLong(kEHCICMDHCReset); IOSync();
for (i=0; (i < 100) && (USBToHostLong(_pEHCIRegisters->USBCMD) & kEHCICMDHCReset); i++)
IOSleep(1);
if (i >= 100)
{
USBError(1, "AppleUSBEHCI[%p]::UIMInitialize - could not get chip to come out of reset within 100 ms", this);
err = kIOReturnInternalError;
break;
}
_pEHCIRegisters->PeriodicListBase = 0; _pEHCIRegisters->AsyncListAddr = 0; IOSync();
lvalue = _device->configRead16(cwCommand);
_device->configWrite16(cwCommand, lvalue | cwCommandEnableBusMaster | cwCommandEnableMemorySpace);
_pEHCIRegisters->USBIntr = HostToUSBLong(kEHCICompleteIntBit | kEHCIErrorIntBit | kEHCIHostErrorIntBit | kEHCIFrListRolloverIntBit);
IOSync();
USBCmd = USBToHostLong(_pEHCIRegisters->USBCMD);
_frameListSize = (USBCmd & kEHCICMDFrameListSizeMask) >> kEHCICMDFrameListSizeOffset;
if (_frameListSize)
{
USBError(1, "AppleUSBEHCI[%p]::UIMInitialize - bad _frameListSize", this);
err = kIOReturnInternalError;
break;
}
_frameListSize = 1024;
USBCmd |= kEHCICMDRunStop;
_ehciBusState = kEHCIBusStateRunning;
USBCmd |= 8 << kEHCICMDIntThresholdOffset; _pEHCIRegisters->USBCMD = USBToHostLong(USBCmd);
_pEHCIRegisters->ConfigFlag = HostToUSBLong(kEHCIPortRoutingBit); IOSync();
IOSleep(1);
if (_errataBits & kErrataNECIncompleteWrite)
{
UInt32 newValue = 0, count = 0;
newValue = USBToHostLong(_pEHCIRegisters->ConfigFlag);
while ((count++ < 10) && (newValue != kEHCIPortRoutingBit))
{
USBError(1, "EHCI driver: UIMInitialize - ConfigFlag bit not sticking. Retrying.");
_pEHCIRegisters->ConfigFlag = HostToUSBLong(kEHCIPortRoutingBit);
IOSync();
newValue = USBToHostLong(_pEHCIRegisters->ConfigFlag);
}
}
_isochBandwidthAvail = 5 *1024;
_outSlot = kEHCIPeriodicListEntries+1;
_frameNumber = 0;
if ((err = InterruptInitialize()))
continue;
_uimInitialized = true;
registerService(); return kIOReturnSuccess;
} while (false);
USBError(1, "AppleUSBEHCI[%p]::UIMInitialize - Error occurred (0x%x)", this, err);
UIMFinalize();
if (_filterInterruptSource)
_filterInterruptSource->release();
return(err);
}
IOReturn
AppleUSBEHCI::AcquireOSOwnership(void)
{
UInt32 hccparams = USBToHostLong(_pEHCICapRegisters->HCCParams & kEHCIEECPMask);
UInt32 eecp;
UInt32 data;
USBLog(2, "Attempting to get EHCI Controller from BIOS");
eecp = (hccparams & kEHCIEECPMask) >> kEHCIEECPPhase;
if (eecp < 0x40)
return kIOReturnSuccess;
data = _device->configRead32(eecp);
if ((data & 0xFF) == kEHCI_USBLEGSUP_ID)
{
USBLog(2, "Found USBLEGSUP_ID - value %p - writing OSOwned", (void*)data);
_device->configWrite32(eecp, data | kEHCI_USBLEGSUP_OSOwned);
for (int i = 0; i < 25; i++)
{
data = _device->configRead32(eecp);
if ((data & kEHCI_USBLEGSUP_BIOSOwned) == 0)
break;
IOSleep(10);
}
if ((_device->configRead32(eecp) & kEHCI_USBLEGSUP_BIOSOwned) != 0)
{
USBError(1, "EHCI controller unable to take control from BIOS");
return kIOReturnNoResources;
}
else
USBLog(2, "acquireOSOwnership done - value %p", (void*)_device->configRead32(eecp));
}
else
{
USBLog(2, "EHCI controller has wrong value in EECP register");
}
return kIOReturnSuccess;
}
IOReturn
AppleUSBEHCI::AsyncInitialize (void)
{
_AsyncHead = NULL;
return kIOReturnSuccess;
}
IOReturn
AppleUSBEHCI::InterruptInitialize (void)
{
int i;
UInt32 termBit, *list;
IOUSBControllerListElement **logical;
IOPhysicalAddress physPtr;
IOReturn status;
UInt64 offset = 0;
IODMACommand::Segment32 segments;
UInt32 numSegments = 1;
IODMACommand *dmaCommand = NULL;
_periodicListBuffer = IOBufferMemoryDescriptor::inTaskWithPhysicalMask(kernel_task, kIOMemoryUnshared | kIODirectionInOut, kEHCIPeriodicFrameListsize, kEHCIStructureAllocationPhysicalMask);
if (_periodicListBuffer == NULL)
{
USBError(1, "AppleUSBEHCI[%p]::InterruptInitialize - IOBufferMemoryDescriptor::inTaskWithPhysicalMask failed", this);
return kIOReturnNoMemory;
}
status = _periodicListBuffer->prepare();
if (status)
{
USBError(1, "AppleUSBEHCI[%p]::InterruptInitialize - could not prepare buffer err(%p)", this, (void*)status);
_periodicListBuffer->release();
_periodicListBuffer = NULL;
return status;
}
_periodicList = (UInt32 *)_periodicListBuffer->getBytesNoCopy();
dmaCommand = IODMACommand::withSpecification(kIODMACommandOutputHost32, 32, PAGE_SIZE, (IODMACommand::MappingOptions)(IODMACommand::kMapped | IODMACommand::kIterateOnly));
if (!dmaCommand)
{
USBError(1, "AppleUSBEHCI[%p]::InterruptInitialize - could not get IODMACommand", this);
_periodicListBuffer->complete();
_periodicListBuffer->release();
_periodicListBuffer = NULL;
return kIOReturnNoMemory;
}
USBLog(6, "AppleUSBEHCI[%p]::InterruptInitialize - got IODMACommand %p", this, dmaCommand);
status = dmaCommand->setMemoryDescriptor(_periodicListBuffer);
if (status)
{
USBError(1, "AppleUSBEHCI[%p]::InterruptInitialize - setMemoryDescriptor returned err (%p)", this, (void*)status);
dmaCommand->release();
_periodicListBuffer->complete();
_periodicListBuffer->release();
_periodicListBuffer = NULL;
return status;
}
status = dmaCommand->gen32IOVMSegments(&offset, &segments, &numSegments);
if (status || (numSegments != 1) || (segments.fLength != PAGE_SIZE))
{
USBError(1, "AppleUSBEHCI[%p]::InterruptInitialize - could not generate segments err (%p) numSegments (%d) fLength (%d)", this, (void*)status, (int)numSegments, (int)segments.fLength);
status = status ? status : kIOReturnInternalError;
dmaCommand->clearMemoryDescriptor();
dmaCommand->release();
_periodicListBuffer->complete();
_periodicListBuffer->release();
_periodicListBuffer = NULL;
return status;
}
physPtr = segments.fIOVMAddr;
USBLog(7, "AppleUSBEHCI[%p]::InterruptInitialize - frame list pPhysical[%p] frames[%p]", this, (void*)physPtr, _periodicList);
_pEHCIRegisters->PeriodicListBase = HostToUSBLong(physPtr);
dmaCommand->clearMemoryDescriptor();
dmaCommand->release();
_logicalPeriodicList = (IOUSBControllerListElement **)IOMalloc(kEHCIPeriodicFrameListsize);
if(_logicalPeriodicList == NULL)
{
_periodicListBuffer->complete();
_periodicListBuffer->release();
_periodicListBuffer = NULL;
return kIOReturnNoMemory;
}
termBit = HostToUSBLong(kEHCITermFlag);
list = (UInt32 *)_periodicList;
logical = _logicalPeriodicList;
for(i= 0; i<kEHCIPeriodicListEntries; i++)
{
*(list++) = termBit;
*(logical++) = NULL;
}
for (i=0; i<kEHCIMaxPoll; i++)
_periodicBandwidth[i] = kEHCIMaxPeriodicBandwidth;
return kIOReturnSuccess;
}
IOReturn
AppleUSBEHCI::UIMFinalize(void)
{
int i;
IOReturn err = kIOReturnSuccess;
if ( _deviceBase )
USBLog (3, "AppleUSBEHCI[%p]: @ %lx (%lx)(shutting down HW)", this, (long)_deviceBase->getVirtualAddress(), _deviceBase->getPhysicalAddress());
if ( _workLoop )
_workLoop->disableAllInterrupts();
IOSleep(2);
if ( _powerDownNotifier ) {
_powerDownNotifier->remove();
_powerDownNotifier = NULL;
}
USBLog(1, "AppleUSBEHCI[%p]: UIMFinalize %x %p %p", this, isInactive(), _pEHCIRegisters, _device);
if ( !isInactive() && _pEHCIRegisters && _device )
{
_pEHCIRegisters->USBIntr = 0x0;
IOSync();
if (_is64bit)
_pEHCIRegisters->CTRLDSSegment = 0; _pEHCIRegisters->USBCMD = 0; IOSync();
for (i=0; (i < 100) && !(USBToHostLong(_pEHCIRegisters->USBSTS) & kEHCIHCHaltedBit); i++)
IOSleep(1);
if (i >= 100)
{
USBError(1, "AppleUSBEHCI[%p]::UIMFinalize - could not get chip to halt within 100 ms", this);
err = kIOReturnInternalError;
goto ErrorExit;
}
_ehciBusState = kEHCIBusStateOff;
_pEHCIRegisters->USBCMD = HostToUSBLong(kEHCICMDHCReset); IOSync();
for (i=0; (i < 100) && (USBToHostLong(_pEHCIRegisters->USBCMD) & kEHCICMDHCReset); i++)
IOSleep(1);
if (i >= 100)
{
USBError(1, "AppleUSBEHCI[%p]::UIMFinalize - could not get chip to come out of reset within 100 ms", this);
err = kIOReturnInternalError;
goto ErrorExit;
}
_pEHCIRegisters->ConfigFlag = 0;
IOSync();
if (_errataBits & kErrataNECIncompleteWrite)
{
UInt32 newValue = 0, count = 0;
newValue = USBToHostLong(_pEHCIRegisters->ConfigFlag);
while ((count++ < 10) && (newValue != 0))
{
USBError(1, "EHCI driver: UIMFinalize - ConfigFlag bit not sticking. Retrying.");
_pEHCIRegisters->ConfigFlag = 0;
IOSync();
newValue = USBToHostLong(_pEHCIRegisters->ConfigFlag);
}
}
_device->configWrite32(cwCommand, cwCommandEnableMemorySpace);
_pEHCIRegisters->PeriodicListBase = 0; _pEHCIRegisters->AsyncListAddr = 0; IOSync();
}
if ( _periodicListBuffer )
{
_periodicListBuffer->complete();
_periodicListBuffer->release();
_periodicListBuffer = NULL;
}
if ( _logicalPeriodicList )
{
IOFree( _logicalPeriodicList, kEHCIPeriodicFrameListsize );
_logicalPeriodicList = NULL;
}
if ( _filterInterruptSource && _workLoop )
{
_workLoop->removeEventSource(_filterInterruptSource);
_filterInterruptSource->release();
_filterInterruptSource = NULL;
}
ErrorExit:
_uimInitialized = false;
return err;
}
UInt32
AppleUSBEHCI::GetBandwidthAvailable()
{
USBLog(7, "AppleUSBEHCI[%p]::GetBandwidthAvailable -- returning %d", this, (int)_isochBandwidthAvail);
return _isochBandwidthAvail;
}
UInt32
AppleUSBEHCI::GetFrameNumber32()
{
UInt32 temp1, temp2;
register UInt32 frindex;
if ( USBToHostLong(_pEHCIRegisters->USBSTS) & kEHCIHCHaltedBit)
{
USBLog(1, "AppleUSBEHCI[%p]::GetFrameNumber32 called but controller is halted", this);
return 0;
}
do
{
temp1 = (UInt32)_frameNumber;
frindex = HostToUSBLong(_pEHCIRegisters->FRIndex);
IOSync();
temp2 = (UInt32)_frameNumber;
} while ((temp1 != temp2) || (frindex == 0) );
frindex = frindex >> 3;
USBLog(7, "AppleUSBEHCI[%p]::GetFrameNumber32 -- returning %d (%p)", this, (int)(temp1+frindex), (void*)(temp1+frindex));
return (UInt32)(temp1 + frindex);
}
UInt64
AppleUSBEHCI::GetFrameNumber()
{
UInt64 temp1, temp2;
register UInt32 frindex;
if ( USBToHostLong(_pEHCIRegisters->USBSTS) & kEHCIHCHaltedBit)
{
USBLog(1, "AppleUSBEHCI[%p]::GetFrameNumber called but controller is halted", this);
return 0;
}
do
{
temp1 = _frameNumber;
frindex = HostToUSBLong(_pEHCIRegisters->FRIndex);
temp2 = _frameNumber;
} while ( (temp1 != temp2) || (frindex == 0) );
frindex = frindex >> 3;
return (temp1 + frindex);
}
UInt64
AppleUSBEHCI::GetMicroFrameNumber()
{
UInt64 temp1, temp2;
register UInt32 frindex;
do
{
temp1 = _frameNumber;
frindex = HostToUSBLong(_pEHCIRegisters->FRIndex);
IOSync();
temp2 = _frameNumber;
} while ( (temp1 != temp2) || (frindex == 0) );
temp1 = temp1 << 3;
USBLog(7, "AppleUSBEHCI[%p]::GetMicroFrameNumber -- returning %Ld (0x%Lx)", this, temp1+frindex, temp1+frindex);
return (temp1 + frindex);
}
void
AppleUSBEHCI::SetVendorInfo(void)
{
OSData *vendProp, *deviceProp, *revisionProp;
vendProp = OSDynamicCast(OSData, _device->getProperty( "vendor-id" ));
if (vendProp)
_vendorID = *((UInt32 *) vendProp->getBytesNoCopy());
deviceProp = OSDynamicCast(OSData, _device->getProperty( "device-id" ));
if (deviceProp)
_deviceID = *((UInt32 *) deviceProp->getBytesNoCopy());
revisionProp = OSDynamicCast(OSData, _device->getProperty( "revision-id" ));
if (revisionProp)
_revisionID = *((UInt32 *) revisionProp->getBytesNoCopy());
}
AppleEHCIQueueHead *
AppleUSBEHCI::AllocateQH(void)
{
AppleEHCIQueueHead *freeQH;
freeQH = _pFreeQH;
if (freeQH == NULL)
{
AppleEHCIedMemoryBlock *memBlock;
UInt32 numEDs, i;
memBlock = AppleEHCIedMemoryBlock::NewMemoryBlock();
if (!memBlock)
{
USBLog(1, "AppleUSBEHCI[%p]::AllocateED - unable to allocate a new memory block!", this);
return NULL;
}
memBlock->SetNextBlock(_edMBHead);
_edMBHead = memBlock;
numEDs = memBlock->NumEDs();
_pLastFreeQH = AppleEHCIQueueHead::WithSharedMemory(memBlock->GetLogicalPtr(0), memBlock->GetPhysicalPtr(0));
_pFreeQH = _pLastFreeQH;
for (i=1; i < numEDs; i++)
{
freeQH = AppleEHCIQueueHead::WithSharedMemory(memBlock->GetLogicalPtr(i), memBlock->GetPhysicalPtr(i));
if (!freeQH)
{
USBLog(1, "AppleUSBEHCI[%p]::AllocateED - hmm. ran out of EDs in a memory block", this);
freeQH = _pFreeQH;
break;
}
freeQH->_logicalNext = _pFreeQH;
_pFreeQH = freeQH;
}
}
if (freeQH)
{
_pFreeQH = OSDynamicCast(AppleEHCIQueueHead, freeQH->_logicalNext);
if (!_pFreeQH)
_pLastFreeQH = NULL;
freeQH->_logicalNext = NULL;
}
return freeQH;
}
IOReturn
AppleUSBEHCI::DeallocateTD (EHCIGeneralTransferDescriptorPtr pTD)
{
UInt32 physical;
physical = pTD->pPhysical;
pTD->pLogicalNext = NULL;
pTD->pPhysical = physical;
if (_pLastFreeTD)
{
_pLastFreeTD->pLogicalNext = pTD;
_pLastFreeTD = pTD;
}
else
{
_pLastFreeTD = pTD;
_pFreeTD = pTD;
}
return kIOReturnSuccess;
}
IOReturn
AppleUSBEHCI::DeallocateED (AppleEHCIQueueHead *pED)
{
USBLog(7, "AppleUSBEHCI[%p]::DeallocateED - deallocating %p", this, pED);
pED->_logicalNext = NULL;
if (_pLastFreeQH)
{
_pLastFreeQH->_logicalNext = pED;
_pLastFreeQH = pED;
}
else
{
_pLastFreeQH = pED;
_pFreeQH = pED;
}
return (kIOReturnSuccess);
}
EHCIGeneralTransferDescriptorPtr
AppleUSBEHCI::AllocateTD(void)
{
EHCIGeneralTransferDescriptorPtr freeTD;
freeTD = _pFreeTD;
if (freeTD == NULL)
{
AppleEHCItdMemoryBlock *memBlock;
UInt32 numTDs, i;
memBlock = AppleEHCItdMemoryBlock::NewMemoryBlock();
if (!memBlock)
{
USBError(1, "AppleUSBEHCI[%p]::AllocateTD - unable to allocate a new memory block!", this);
return NULL;
}
memBlock->SetNextBlock(_tdMBHead);
_tdMBHead = memBlock;
numTDs = memBlock->NumTDs();
_pLastFreeTD = memBlock->GetTD(0); for (i=0; i < numTDs; i++)
{
freeTD = memBlock->GetTD(i);
if (!freeTD)
{
USBError(1, "AppleUSBEHCI[%p]::AllocateTD - hmm. ran out of TDs in a memory block", this);
freeTD = _pFreeTD;
break;
}
freeTD->pLogicalNext = _pFreeTD;
_pFreeTD = freeTD;
}
}
if (freeTD)
{
_pFreeTD = freeTD->pLogicalNext;
if (!_pFreeTD)
_pLastFreeTD = NULL;
freeTD->pLogicalNext = NULL;
freeTD->lastFrame = 0;
freeTD->lastRemaining = 0;
freeTD->command = NULL;
}
return freeTD;
}
AppleEHCIIsochTransferDescriptor *
AppleUSBEHCI::AllocateITD(void)
{
AppleEHCIIsochTransferDescriptor *freeITD;
freeITD = _pFreeITD;
if (freeITD == NULL)
{
AppleEHCIitdMemoryBlock *memBlock;
UInt32 numTDs, i;
memBlock = AppleEHCIitdMemoryBlock::NewMemoryBlock();
if (!memBlock)
{
USBError(1, "AppleUSBEHCI[%p]::AllocateITD - unable to allocate a new memory block!", this);
return NULL;
}
memBlock->SetNextBlock(_itdMBHead);
_itdMBHead = memBlock;
numTDs = memBlock->NumTDs();
_pLastFreeITD = AppleEHCIIsochTransferDescriptor::WithSharedMemory(memBlock->GetLogicalPtr(0), memBlock->GetPhysicalPtr(0));
_pFreeITD = _pLastFreeITD;
for (i=1; i < numTDs; i++)
{
freeITD = AppleEHCIIsochTransferDescriptor::WithSharedMemory(memBlock->GetLogicalPtr(i), memBlock->GetPhysicalPtr(i));
if (!freeITD)
{
USBError(1, "AppleUSBEHCI[%p]::AllocateTD - hmm. ran out of TDs in a memory block", this);
freeITD = _pFreeITD;
break;
}
freeITD->_logicalNext = _pFreeITD;
_pFreeITD = freeITD;
}
}
if (freeITD)
{
_pFreeITD = OSDynamicCast(AppleEHCIIsochTransferDescriptor, freeITD->_logicalNext);
freeITD->_logicalNext = NULL;
if (!_pFreeITD)
_pLastFreeITD = NULL;
}
bzero(&freeITD->GetSharedLogical()->Transaction0, sizeof(EHCIIsochTransferDescriptorShared)-sizeof(IOPhysicalAddress) );
USBLog(7, "AppleUSBEHCI[%p]::AllocateITD - returning %p", this, freeITD);
return freeITD;
}
IOReturn
AppleUSBEHCI::DeallocateITD (AppleEHCIIsochTransferDescriptor *pTD)
{
USBLog(7, "AppleUSBEHCI[%p]::DeallocateITD - deallocating %p", this, pTD);
pTD->_logicalNext = NULL;
if (_pLastFreeITD)
{
_pLastFreeITD->_logicalNext = pTD;
_pLastFreeITD = pTD;
}
else
{
_pLastFreeITD = pTD;
_pFreeITD = pTD;
}
return kIOReturnSuccess;
}
AppleEHCISplitIsochTransferDescriptor *
AppleUSBEHCI::AllocateSITD(void)
{
AppleEHCISplitIsochTransferDescriptor *freeSITD;
freeSITD = _pFreeSITD;
if (freeSITD == NULL)
{
AppleEHCIsitdMemoryBlock *memBlock;
UInt32 numTDs, i;
memBlock = AppleEHCIsitdMemoryBlock::NewMemoryBlock();
if (!memBlock)
{
USBError(1, "AppleUSBEHCI[%p]::AllocateTD - unable to allocate a new memory block!", this);
return NULL;
}
memBlock->SetNextBlock(_sitdMBHead);
_sitdMBHead = memBlock;
numTDs = memBlock->NumTDs();
USBLog(3, "AppleUSBEHCI[%p]::AllocateSITD - got new memory block (%p) with %d SITDs in it", this, memBlock, (int)numTDs);
_pLastFreeSITD = AppleEHCISplitIsochTransferDescriptor::WithSharedMemory(memBlock->GetLogicalPtr(0), memBlock->GetPhysicalPtr(0));
_pFreeSITD = _pLastFreeSITD;
for (i=1; i < numTDs; i++)
{
freeSITD = AppleEHCISplitIsochTransferDescriptor::WithSharedMemory(memBlock->GetLogicalPtr(i), memBlock->GetPhysicalPtr(i));
if (!freeSITD)
{
USBError(1, "AppleUSBEHCI[%p]::AllocateTD - hmm. ran out of TDs in a memory block", this);
freeSITD = _pFreeSITD;
break;
}
freeSITD->_logicalNext = _pFreeSITD;
_pFreeSITD = freeSITD;
}
}
if (freeSITD)
{
_pFreeSITD = OSDynamicCast(AppleEHCISplitIsochTransferDescriptor, freeSITD->_logicalNext);
if (!_pFreeSITD)
_pLastFreeSITD = NULL;
freeSITD->_logicalNext = NULL;
freeSITD->_isDummySITD = false;
}
USBLog(7, "AppleUSBEHCI[%p]::AllocateSITD - returning %p", this, freeSITD);
return freeSITD;
}
IOReturn
AppleUSBEHCI::DeallocateSITD (AppleEHCISplitIsochTransferDescriptor *pTD)
{
USBLog(7, "AppleUSBEHCI[%p]::DeallocateSITD - deallocating %p", this, pTD);
pTD->_logicalNext = NULL;
if (_pLastFreeSITD)
{
_pLastFreeSITD->_logicalNext = pTD;
_pLastFreeSITD = pTD;
}
else
{
_pLastFreeSITD = pTD;
_pFreeSITD = pTD;
}
return kIOReturnSuccess;
}
void
AppleUSBEHCI::doCallback(EHCIGeneralTransferDescriptorPtr nextTD,
UInt32 transferStatus,
UInt32 bufferSizeRemaining)
{
USBError(1, "AppleUSBEHCI[%p]::doCallback unimemented *************", this);
}
IOReturn
AppleUSBEHCI::UIMInitializeForPowerUp(void)
{
UInt32 CapLength, USBCmd;
IOReturn err = kIOReturnSuccess;
UInt16 lvalue;
int i;
USBLog(5, "AppleUSBEHCI[%p]:UIMInitializeForPowerUp - @ %p (%p)", this, (void*)_deviceBase->getVirtualAddress(), (void*)_deviceBase->getPhysicalAddress());
do {
lvalue = _device->configRead16(cwCommand);
_device->configWrite16(cwCommand, lvalue | cwCommandEnableMemorySpace);
CapLength = _pEHCICapRegisters->CapLength;
_pEHCIRegisters = (EHCIRegistersPtr) ( ((UInt32)_pEHCICapRegisters) + CapLength);
_workLoop->enableAllInterrupts();
_rootHubFuncAddress = 1;
_pEHCIRegisters->USBCMD = 0; IOSync();
for ( i = 0; (i < 100) && !(USBToHostLong(_pEHCIRegisters->USBSTS) & kEHCIHCHaltedBit); i++ )
IOSleep(1);
if ( i >= 100 )
{
USBError(1, "AppleUSBEHCI[%p]::UIMInitialize - could not get chip to halt within 100 ms", this);
err = kIOReturnInternalError;
break;
}
_pEHCIRegisters->USBCMD = HostToUSBLong(kEHCICMDHCReset);
IOSync();
for ( i = 0; (i < 100) && (USBToHostLong(_pEHCIRegisters->USBCMD) & kEHCICMDHCReset); i++ )
IOSleep(1);
if ( i >= 100 )
{
USBError(1, "AppleUSBEHCI[%p]::UIMInitializeForPowerUp - could not get chip to come out of reset within 100 ms", this);
err = kIOReturnInternalError;
break;
}
if (_pEHCIRegisters->PeriodicListBase != _savedPeriodicListBase)
{
USBLog(1, "AppleUSBEHCI[%p]::UIMInitializeForPowerUp - restoring PeriodicListBase[from 0x%lx to 0x%lx]", this, _pEHCIRegisters->PeriodicListBase, _savedPeriodicListBase);
_pEHCIRegisters->PeriodicListBase = _savedPeriodicListBase;
IOSync();
}
if (_pEHCIRegisters->AsyncListAddr != _savedAsyncListAddr)
{
USBLog(1, "AppleUSBEHCI[%p]::UIMInitializeForPowerUp - restoring AsyncListAddr[from 0x%lx to 0x%lx]", this, _pEHCIRegisters->AsyncListAddr, _savedAsyncListAddr);
_pEHCIRegisters->AsyncListAddr = _savedAsyncListAddr;
IOSync();
}
lvalue = _device->configRead16(cwCommand);
_device->configWrite16(cwCommand, lvalue | (cwCommandEnableBusMaster | cwCommandEnableMemorySpace));
_pEHCIRegisters->USBIntr = HostToUSBLong(kEHCICompleteIntBit | kEHCIErrorIntBit | kEHCIHostErrorIntBit | kEHCIFrListRolloverIntBit);
IOSync();
USBCmd = USBToHostLong(_pEHCIRegisters->USBCMD);
_frameListSize = (USBCmd & kEHCICMDFrameListSizeMask) >> kEHCICMDFrameListSizeOffset;
if (_frameListSize)
{
USBError(1, "AppleUSBEHCI[%p]::UIMInitialize - bad _frameListSize", this);
err = kIOReturnInternalError;
break;
}
_frameListSize = 1024;
USBCmd |= kEHCICMDRunStop;
_ehciBusState = kEHCIBusStateRunning;
USBCmd |= 8 << kEHCICMDIntThresholdOffset; _pEHCIRegisters->USBCMD = HostToUSBLong(USBCmd);
_pEHCIRegisters->ConfigFlag = HostToUSBLong(kEHCIPortRoutingBit); IOSync();
if (_errataBits & kErrataNECIncompleteWrite)
{
UInt32 newValue = 0, count = 0;
newValue = USBToHostLong(_pEHCIRegisters->ConfigFlag);
while ((count++ < 10) && (newValue != kEHCIPortRoutingBit))
{
USBError(1, "EHCI driver: UIMInitializeForPowerUp - ConfigFlag bit not sticking. Retrying.");
_pEHCIRegisters->ConfigFlag = HostToUSBLong(kEHCIPortRoutingBit);
IOSync();
newValue = USBToHostLong(_pEHCIRegisters->ConfigFlag);
}
}
_isochBandwidthAvail = 5 *1024;
_outSlot = kEHCIPeriodicListEntries+1;
_frameNumber = 0;
_uimInitialized = true;
return kIOReturnSuccess;
} while (false);
USBError(1, "AppleUSBEHCI[%p]::UIMInitializeForPowerUp - Error occurred (0x%x)", this, err);
UIMFinalize();
if (_filterInterruptSource)
_filterInterruptSource->release();
return err;
}
IOReturn
AppleUSBEHCI::UIMFinalizeForPowerDown(void)
{
int i;
IOReturn err;
if ( _deviceBase )
USBLog (3, "AppleUSBEHCI[%p]: @ %lx (%lx)(turning off HW)", this, (long)_deviceBase->getVirtualAddress(), _deviceBase->getPhysicalAddress());
if ( _workLoop )
_workLoop->disableAllInterrupts();
IOSleep(2);
if ( _pEHCIRegisters && _device )
{
_pEHCIRegisters->USBIntr = 0x0;
IOSync();
_pEHCIRegisters->USBCMD = 0; for (i=0; (i < 100) && !(USBToHostLong(_pEHCIRegisters->USBSTS) & kEHCIHCHaltedBit); i++)
IOSleep(1);
if (i >= 100)
{
USBError(1, "AppleUSBEHCI[%p]::UIMInitializeForPowerUp - could not get chip to halt within 100 ms", this);
err = kIOReturnInternalError;
goto ErrorExit;
}
_ehciBusState = kEHCIBusStateOff;
_pEHCIRegisters->USBCMD = HostToUSBLong(kEHCICMDHCReset); for (i=0; (i < 100) && (USBToHostLong(_pEHCIRegisters->USBCMD) & kEHCICMDHCReset); i++)
IOSleep(1);
if (i >= 100)
{
USBError(1, "AppleUSBEHCI[%p]::UIMInitializeForPowerUp - could not get chip to come out of reset within 100 ms", this);
err = kIOReturnInternalError;
goto ErrorExit;
}
_pEHCIRegisters->ConfigFlag = 0;
IOSync();
IOSleep(1);
if (_errataBits & kErrataNECIncompleteWrite)
{
UInt32 newValue = 0, count = 0;
newValue = USBToHostLong(_pEHCIRegisters->ConfigFlag);
while ((count++ < 10) && (newValue != 0))
{
USBError(1, "EHCI driver: UIMFinalizeForPowerDown - ConfigFlag bit not sticking. Retrying.");
_pEHCIRegisters->ConfigFlag = 0;
IOSync();
newValue = USBToHostLong(_pEHCIRegisters->ConfigFlag);
}
}
_device->configWrite16(cwCommand, cwCommandEnableMemorySpace);
_pEHCIRegisters->PeriodicListBase = 0; _pEHCIRegisters->AsyncListAddr = 0; IOSync();
}
ErrorExit:
_uimInitialized = false;
return kIOReturnSuccess;
}
IOReturn
AppleUSBEHCI::EnableAsyncSchedule(bool waitForON)
{
int i;
IOReturn stat = kIOReturnSuccess;
if (!(_pEHCIRegisters->USBCMD & HostToUSBLong(kEHCICMDAsyncEnable)))
{
USBLog(7, "AppleUSBEHCI[%p]::EnableAsyncSchedule: enabling schedule", this);
if (_errataBits & kErrataAgereEHCIAsyncSched)
{
_pEHCIRegisters->USBCMD &= HostToUSBLong(~kEHCICMDRunStop);
while (!(USBToHostLong(_pEHCIRegisters->USBSTS) & kEHCIHCHaltedBit))
;
_pEHCIRegisters->USBCMD |= HostToUSBLong(kEHCICMDAsyncEnable | kEHCICMDRunStop);
}
else
{
_pEHCIRegisters->USBCMD |= HostToUSBLong(kEHCICMDAsyncEnable);
}
if (waitForON) {
for (i=0; (i < 100) && !(USBToHostLong(_pEHCIRegisters->USBSTS) & kEHCISTSAsyncScheduleStatus); i++)
IOSleep(1);
if (i)
{
if (i >= 100)
{
USBError(1, "AppleUSBEHCI[%p]::EnableAsyncSchedule: ERROR: USBCMD and USBSTS won't synchronize ON - cannot enable USB list processing", this);
stat = kIOReturnInternalError;
}
else
{
USBLog(7, "AppleUSBEHCI[%p]::EnableAsyncSchedule: had to wait %d ms for CMD and STS to synch up ON", this, i);
}
}
}
}
else
{
USBLog(7, "AppleUSBEHCI[%p]::EnableAsyncSchedule: schedule was already enabled", this);
}
if (stat)
{
USBLog(1, "AppleUSBEHCI[%p]::EnableAsyncSchedule: returning status %x", this, stat);
}
else
{
USBLog(7, "AppleUSBEHCI[%p]::EnableAsyncSchedule: schedule enabled cleanly", this);
}
return stat;
}
IOReturn
AppleUSBEHCI::DisableAsyncSchedule(bool waitForOFF)
{
int i;
IOReturn stat = kIOReturnSuccess;
if (_pEHCIRegisters->USBCMD & HostToUSBLong(kEHCICMDAsyncEnable))
{
USBLog(7, "AppleUSBEHCI[%p]::DisableAsyncSchedule: disabling schedule", this);
_pEHCIRegisters->USBCMD &= HostToUSBLong(~kEHCICMDAsyncEnable);
if (waitForOFF) {
for (i=0; (i < 100) && (USBToHostLong(_pEHCIRegisters->USBSTS) & kEHCISTSAsyncScheduleStatus); i++)
IOSleep(1);
if (i)
{
if (i >= 100)
{
USBLog(1, "AppleUSBEHCI[%p]::DisableAsyncSchedule: ERROR: USBCMD and USBSTS won't synchronize OFF", this);
stat = kIOReturnInternalError;
}
else
{
USBLog(7, "AppleUSBEHCI[%p]::DisableAsyncSchedule: had to wait %d ms for CMD and STS to synch up OFF", this, i);
}
}
}
}
else
{
USBLog(7, "AppleUSBEHCI[%p]::DisableAsyncSchedule: schedule was already disabled", this);
}
if (stat)
{
USBLog(1, "AppleUSBEHCI[%p]::DisableAsyncSchedule: returning status %x", this, stat);
}
else
{
USBLog(7, "AppleUSBEHCI[%p]::DisableAsyncSchedule: schedule disabled cleanly", this);
}
return stat;
}
IOReturn
AppleUSBEHCI::EnablePeriodicSchedule(bool waitForON)
{
int i;
IOReturn stat = kIOReturnSuccess;
if (_inAbortIsochEP)
{
USBLog(2, "EnablePeriodicSchedule - not doing in AbortIsochEP");
return stat;
}
if (!(_pEHCIRegisters->USBCMD & HostToUSBLong(kEHCICMDPeriodicEnable)))
{
USBLog(7, "AppleUSBEHCI[%p]::EnablePeriodicSchedule: enabling schedule", this);
_pEHCIRegisters->USBCMD |= HostToUSBLong(kEHCICMDPeriodicEnable);
if (waitForON) {
for (i=0; (i < 100) && !(USBToHostLong(_pEHCIRegisters->USBSTS) & kEHCISTSPeriodicScheduleStatus); i++)
IOSleep(1);
if (i)
{
if (i >= 100)
{
USBLog(1, "AppleUSBEHCI[%p]::EnablePeriodicSchedule: ERROR: USBCMD and USBSTS won't synchronize ON", this);
stat = kIOReturnInternalError;
}
else
{
USBLog(7, "AppleUSBEHCI[%p]::EnablePeriodicSchedule: had to wait %d ms for CMD and STS to synch up ON", this, i);
}
}
}
}
else
{
USBLog(7, "AppleUSBEHCI[%p]::EnablePeriodicSchedule: schedule was already enabled", this);
}
if (stat)
{
USBLog(1, "AppleUSBEHCI[%p]::EnablePeriodicSchedule: returning status %x", this, stat);
}
else
{
USBLog(7, "AppleUSBEHCI[%p]::EnablePeriodicSchedule: schedule enabled cleanly", this);
}
return stat;
}
IOReturn
AppleUSBEHCI::DisablePeriodicSchedule(bool waitForOFF)
{
int i;
IOReturn stat = kIOReturnSuccess;
if (_pEHCIRegisters->USBCMD & HostToUSBLong(kEHCICMDPeriodicEnable))
{
USBLog(7, "AppleUSBEHCI[%p]::DisablePeriodicSchedule: disabling schedule", this);
_pEHCIRegisters->USBCMD &= HostToUSBLong(~kEHCICMDPeriodicEnable);
if (waitForOFF) {
for (i=0; (i < 100) && (USBToHostLong(_pEHCIRegisters->USBSTS) & kEHCISTSPeriodicScheduleStatus); i++)
IOSleep(1);
if (i)
{
if (i >= 100)
{
USBLog(1, "AppleUSBEHCI[%p]::DisablePeriodicSchedule: ERROR: USBCMD and USBSTS won't synchronize OFF", this);
stat = kIOReturnInternalError;
}
else
{
USBLog(7, "AppleUSBEHCI[%p]::DisablePeriodicSchedule: had to wait %d ms for CMD and STS to synch up OFF", this, i);
}
}
}
}
else
{
USBLog(7, "AppleUSBEHCI[%p]::DisablePeriodicSchedule: schedule was already disabled", this);
}
if (stat)
{
USBLog(1, "AppleUSBEHCI[%p]::DisablePeriodicSchedule: returning status %x", this, stat);
}
else
{
USBLog(7, "AppleUSBEHCI[%p]::DisablePeriodicSchedule: schedule disabled cleanly", this);
}
return stat;
}
IOReturn
AppleUSBEHCI::message( UInt32 type, IOService * provider, void * argument )
{
USBLog(6, "AppleUSBEHCI[%p]::message type: %p, isInactive = %d", this, (void*)type, isInactive());
if (type == kIOUSBMessageExpressCardCantWake)
{
IOService * nub = (IOService*)argument;
const IORegistryPlane * usbPlane = getPlane(kIOUSBPlane);
IOUSBRootHubDevice * parentHub = OSDynamicCast(IOUSBRootHubDevice, nub->getParentEntry(usbPlane));
nub->retain();
USBLog(1, "AppleUSBUHCI[%p]::message - got kIOUSBMessageExpressCardCantWake from driver %s[%p] argument is %s[%p]", this, provider->getName(), provider, nub->getName(), nub);
if (parentHub == _rootHubDevice)
{
USBLog(1, "AppleUSBUHCI[%p]::message - device is attached to my root hub!!", this);
_badExpressCardAttached = true;
}
nub->release();
return kIOReturnSuccess; }
if ( type == kIOPCCardCSEventMessage)
{
cs_event_t pccardevent;
pccardevent = (UInt32) argument;
if ( pccardevent == CS_EVENT_CARD_REMOVAL )
{
USBLog(5,"AppleUSBEHCI[%p]: Received kIOPCCardCSEventMessage Need to return all transactions",this);
_pcCardEjected = true;
}
}
return super::message( type, provider, argument );
}
IOUSBControllerIsochEndpoint*
AppleUSBEHCI::AllocateIsochEP()
{
AppleEHCIIsochEndpoint *pEP;
pEP = new AppleEHCIIsochEndpoint;
if (pEP)
{
if (!pEP->init())
{
pEP->release();
pEP = NULL;
}
}
return pEP;
}
void
AppleUSBEHCI::ReturnIsochDoneQueue(IOUSBControllerIsochEndpoint* isochEP)
{
super::ReturnIsochDoneQueue(isochEP);
}
IODMACommand*
AppleUSBEHCI::GetNewDMACommand()
{
USBLog(7, "AppleUSBEHCI[%p]::GetNewDMACommand - creating %d bit IODMACommand", this, _is64bit ? 64 : 32);
return IODMACommand::withSpecification(kIODMACommandOutputHost64, _is64bit ? 64 : 32, 0);
}