AppleUSBEHCIHubInfo.cpp [plain text]
#include <IOKit/IOTypes.h>
#include <IOKit/usb/IOUSBLog.h>
#include "AppleUSBEHCI.h"
#include "AppleUSBEHCIHubInfo.h"
OSDefineMetaClassAndStructors(AppleUSBEHCIHubInfo, OSObject)
AppleUSBEHCIHubInfo*
AppleUSBEHCIHubInfo::GetHubInfo(AppleUSBEHCIHubInfo **hubListPtr, USBDeviceAddress hubAddr, int hubPort)
{
AppleUSBEHCIHubInfo *hiList = *hubListPtr;
AppleUSBEHCIHubInfo *hiPtr;
hiPtr = FindHubInfo(hiList, hubAddr, hubPort);
if (!hiPtr)
{
USBLog(7, "AppleUSBEHCIHubInfo::GetHubInfo - addr: %d port %d not found, looking for master", hubAddr, hubPort);
hiPtr = FindHubInfo(hiList, hubAddr, 0);
if (hiPtr && (hiPtr->flags & kUSBEHCIFlagsMuliTT))
{
USBLog(7, "AppleUSBEHCIHubInfo::GetHubInfo - creating info for addr: %d port %d", hubAddr, hubPort);
hiPtr = NewHubInfo(hubAddr, hubPort);
if (hiPtr)
{
hiPtr->next = hiList;
hiList = hiPtr;
}
}
}
*hubListPtr = hiList;
return hiPtr;
}
AppleUSBEHCIHubInfo*
AppleUSBEHCIHubInfo::NewHubInfoZero (AppleUSBEHCIHubInfo **hubListPtr, USBDeviceAddress hubAddr, UInt32 flags)
{
AppleUSBEHCIHubInfo *hiList = *hubListPtr;
AppleUSBEHCIHubInfo *hiPtr;
USBLog(3, "AppleUSBEHCIHubInfo::NewHubInfoZero - addr: %d", hubAddr);
hiPtr = NewHubInfo(hubAddr, 0);
if (!hiPtr)
return NULL;
hiPtr->flags = flags;
hiPtr->next = hiList;
hiList = hiPtr;
*hubListPtr = hiList;
return hiPtr;
}
AppleUSBEHCIHubInfo*
AppleUSBEHCIHubInfo::FindHubInfo(AppleUSBEHCIHubInfo *hiPtr, USBDeviceAddress hubAddr, int hubPort)
{
while (hiPtr)
{
if ((hiPtr->hubAddr == hubAddr) && (hiPtr->hubPort == hubPort))
break;
hiPtr = hiPtr->next;
}
USBLog(5, "AppleUSBEHCIHubInfo::FindHubInfo(%d, %d), returning %p", hubAddr, hubPort, hiPtr);
return hiPtr;
}
AppleUSBEHCIHubInfo *
AppleUSBEHCIHubInfo::NewHubInfo(USBDeviceAddress hubAddr, int hubPort)
{
AppleUSBEHCIHubInfo *hiPtr = new AppleUSBEHCIHubInfo;
int i;
if (!hiPtr)
return NULL;
hiPtr->hubAddr = hubAddr;
hiPtr->hubPort = hubPort;
hiPtr->next = NULL;
hiPtr->flags = 0;
for (i=0; i < 8; i++)
{
hiPtr->isochOUTUsed[i] = 0;
hiPtr->isochINUsed[i] = 0;
hiPtr->interruptUsed[i] = 0;
}
return hiPtr;
}
IOReturn
AppleUSBEHCIHubInfo::DeleteHubInfoZero(AppleUSBEHCIHubInfo **hubListPtr, USBDeviceAddress hubAddress)
{
AppleUSBEHCIHubInfo *hiList = *hubListPtr;
AppleUSBEHCIHubInfo *hiPtr, *tempPtr;
while (hiList && (hiList->hubAddr == hubAddress))
{
hiPtr = hiList;
hiList = hiList->next;
USBLog(5, "AppleUSBEHCIHubInfo::DeleteHubInfoZero- releasing %p from head of list", hiPtr);
hiPtr->release();
}
hiPtr = hiList;
while (hiPtr && (hiPtr->next))
{
if (hiPtr->next->hubAddr == hubAddress)
{
tempPtr = hiPtr->next;
hiPtr->next = tempPtr->next;
USBLog(5, "AppleUSBEHCIHubInfo::DeleteHubInfoZero- releasing %p from middle of list", tempPtr);
tempPtr->release();
}
else
hiPtr = hiPtr->next;
}
*hubListPtr = hiList; return kIOReturnSuccess;
}
UInt32
AppleUSBEHCIHubInfo::AvailableInterruptBandwidth()
{
int i;
UInt32 avail = 0;
if (!isochINUsed[0] && !isochOUTUsed[0])
{
avail = kUSBEHCIMaxMicroframePeriodic - interruptUsed[0];
for (i=0; i<4; i++)
{
if (isochINUsed[i] || isochOUTUsed[i])
break;
avail += (kUSBEHCIMaxMicroframePeriodic - interruptUsed[i]);
}
}
USBLog(3, "AppleUSBEHCIHubInfo[%p]::AvailableInterruptBandwidth, returning %d", this, avail);
return avail;
}
UInt32
AppleUSBEHCIHubInfo::AvailableIsochBandwidth(UInt32 direction)
{
UInt32 availBandwidth = 0, maxSegment = 0;
int i, thisFrameSize;
bool firstSegment = true, lastSegment=false;
switch (direction)
{
case kUSBOut:
for (i=0; i<8; i++)
{
if (interruptUsed[i])
continue;
USBLog(3, "AppleUSBEHCIHubInfo[%p]::AvailableIsochBandwidth OUT, microframe %d is using %d bytes OUT and %d bytes IN", this, i, isochOUTUsed[i], isochINUsed[i]);
if (isochINUsed[i])
{
thisFrameSize = (kUSBEHCIMaxMicroframePeriodic - isochINUsed[i]);
lastSegment = true;
if ((thisFrameSize + isochOUTUsed[i]) > kUSBEHCIMaxMicroframePeriodic)
thisFrameSize = kUSBEHCIMaxMicroframePeriodic - isochOUTUsed[i];
}
else if (isochOUTUsed[i])
{
thisFrameSize = kUSBEHCIMaxMicroframePeriodic - isochOUTUsed[i];
if (!firstSegment)
lastSegment = true;
}
else
thisFrameSize = kUSBEHCIMaxMicroframePeriodic;
if (thisFrameSize)
firstSegment = false;
maxSegment += thisFrameSize;
if (lastSegment)
{
if (maxSegment > availBandwidth)
availBandwidth = maxSegment;
maxSegment = 0;
firstSegment = true;
lastSegment = false;
}
}
break;
case kUSBIn:
if (!isochINUsed[0])
maxSegment = kUSBEHCIMaxMicroframePeriodic / 2;
if ((maxSegment + interruptUsed[0]) > kUSBEHCIMaxMicroframePeriodic)
maxSegment = kUSBEHCIMaxMicroframePeriodic - interruptUsed[0];
USBLog(3, "AppleUSBEHCIHubInfo[%p]::AvailableIsochBandwidth IN, microframe 0 has %d bytes available", this, maxSegment);
if (maxSegment)
firstSegment = false;
for (i = 7; i > 0; i--)
{
USBLog(3, "AppleUSBEHCIHubInfo[%p]::AvailableIsochBandwidth IN, microframe %d is using %d bytes OUT and %d bytes IN", this, i, isochOUTUsed[i], isochINUsed[i]);
if (isochOUTUsed[i])
{
thisFrameSize = 0;
lastSegment = true;
}
else if (isochINUsed[i])
{
thisFrameSize = kUSBEHCIMaxMicroframePeriodic - isochINUsed[i];
if (!firstSegment)
lastSegment = true;
}
else
thisFrameSize = kUSBEHCIMaxMicroframePeriodic;
maxSegment += thisFrameSize;
if (lastSegment)
{
if (maxSegment > availBandwidth)
availBandwidth = maxSegment;
maxSegment = 0;
firstSegment = true;
lastSegment = false;
}
}
break;
default:
USBLog(1, "AppleUSBEHCIHubInfo[%p]::AvailableIsochBandwidth, invalid direction %d", this, direction);
}
if (maxSegment > availBandwidth)
availBandwidth = maxSegment;
USBLog(3, "AppleUSBEHCIHubInfo[%p]::AvailableIsochBandwidth, returning %d", this, availBandwidth);
return availBandwidth;
}
IOReturn
AppleUSBEHCIHubInfo::AllocateInterruptBandwidth(AppleEHCIQueueHead *pQH, UInt32 maxPacketSize)
{
UInt32 mySMask = 0;
UInt8 myStartFrame; int i;
UInt32 thisFrameSize;
USBLog(3, "AppleUSBEHCIHubInfo[%p]::AllocateInterruptBandwidth: allocating %d", this, maxPacketSize);
for (i=0; i<4; i++)
{
if (isochINUsed[i] || isochOUTUsed[i])
break;
if ((interruptUsed[i] + kUSBEHCIFSPeriodicOverhead) > kUSBEHCIMaxMicroframePeriodic)
continue;
thisFrameSize = kUSBEHCIMaxMicroframePeriodic - interruptUsed[i];
if (thisFrameSize > maxPacketSize)
thisFrameSize = maxPacketSize;
maxPacketSize -= thisFrameSize;
myStartFrame = i;
if (maxPacketSize)
{
if (i == 3)
break;
if (isochINUsed[i+1] || isochOUTUsed[i+1])
break;
if ((interruptUsed[i+1] + maxPacketSize + kUSBEHCIFSPeriodicOverhead) > kUSBEHCIMaxMicroframePeriodic)
break;
}
USBLog(3, "AppleUSBEHCIHubInfo[%p]::AllocateInterruptBandwidth: assigning %d bytes to microframe %d", this, thisFrameSize, i);
interruptUsed[i] += (thisFrameSize + kUSBEHCIFSPeriodicOverhead);
pQH->_bandwidthUsed[i] = thisFrameSize + kUSBEHCIFSPeriodicOverhead;
mySMask = (0x01 << (i+kEHCIEDSplitFlags_SMaskPhase)); mySMask |= (0x1C << (i+kEHCIEDSplitFlags_CMaskPhase)); if (!maxPacketSize)
break;
}
if (maxPacketSize)
return kIOReturnNoBandwidth;
pQH->GetSharedLogical()->splitFlags |= HostToUSBLong(mySMask);
return kIOReturnSuccess;
}
IOReturn
AppleUSBEHCIHubInfo::DeallocateInterruptBandwidth(AppleEHCIQueueHead *pQH)
{
int i;
if (pQH)
{
for (i=0; i<4; i++)
{
if (pQH->_bandwidthUsed[i])
{
USBLog(3, "AppleUSBEHCIHubInfo[%p]::DeallocateInterruptBandwidth: deallocating %d bytes from microframe %d", this, pQH->_bandwidthUsed[i], i);
interruptUsed[i] -= pQH->_bandwidthUsed[i];
pQH->_bandwidthUsed[i] = 0;
}
}
}
return kIOReturnSuccess;
}
IOReturn
AppleUSBEHCIHubInfo::AllocateIsochBandwidth(AppleEHCIIsochEndpointPtr pEP, UInt32 maxPacketSize)
{
UInt32 remainder = maxPacketSize;
UInt32 thisFrameSize;
UInt8 startSplitFlags = 0, completeSplitFlags = 0, completeSplitsNeeded = 0, SSframe = 255, CSframe=0;
UInt8 framesUsed[8];
UInt8 amountsUsed[8];
int i,j, frameIndex;
if (maxPacketSize == 0)
return kIOReturnSuccess;
if (AvailableIsochBandwidth(pEP->direction) < maxPacketSize)
{
USBLog(3, "AppleUSBEHCIHubInfo[%p]::AllocateIsochBandwidth - maxPacketSize greater than known available - returning no bandwidth", this);
return kIOReturnNoBandwidth;
}
for (i=0; i<8; i++)
{
framesUsed[i] = 255;
amountsUsed[i] = 255;
}
switch (pEP->direction)
{
case kUSBIn:
USBLog(3, "AppleUSBEHCIHubInfo[%p]::AllocateIsochBandwidth: allocating %d bytes IN", this, maxPacketSize);
frameIndex = 0;
if (remainder > ((5*kUSBEHCIMaxMicroframePeriodic)+2))
{
for(i=0; i< 8; i++)
if (isochOUTUsed[i] || interruptUsed[i] || isochINUsed[i])
{
USBLog(3, "AppleUSBEHCIHubInfo[%p]::AllocateIsochBandwidth: allocating %d bytes IN", this, maxPacketSize);
return kIOReturnNoBandwidth;
}
i=0;
thisFrameSize = kUSBEHCIMaxMicroframePeriodic;
while (remainder)
{
if (remainder < kUSBEHCIMaxMicroframePeriodic)
thisFrameSize = remainder;
remainder -= thisFrameSize;
framesUsed[frameIndex]=i;
amountsUsed[frameIndex++] = thisFrameSize;
}
completeSplitFlags = 0xFD; startSplitFlags = 1; pEP->useBackPtr = true;
}
else
{
for (i = 7; i > 1; i--)
{
if ((isochINUsed[i] < kUSBEHCIMaxMicroframePeriodic) && (isochOUTUsed[i] == 0))
{
if ((isochINUsed[i] + remainder) < kUSBEHCIMaxMicroframePeriodic)
thisFrameSize = remainder;
else
thisFrameSize = kUSBEHCIMaxMicroframePeriodic - isochINUsed[i];
if (SSframe > i)
SSframe = i;
completeSplitsNeeded++;
USBLog(3, "AppleUSBEHCIHubInfo::AllocateIsochBandwidth - frame %d uses %d bytes, total completesplits is now %d (SSframe is %d)", i, thisFrameSize, completeSplitsNeeded, SSframe);
remainder -= thisFrameSize;
framesUsed[frameIndex] = i;
amountsUsed[frameIndex++] = thisFrameSize;
if (!remainder)
{
startSplitFlags = 1 << (SSframe-1);
completeSplitFlags = 0;
completeSplitsNeeded++; USBLog(3, "AppleUSBEHCIHubInfo::AllocateIsochBandwidth - extra completesplits is now %d (SSFrame = %d)", completeSplitsNeeded, SSframe);
CSframe = (SSframe + 1) % 8; while (completeSplitsNeeded--)
{
if (CSframe == 0)
pEP->useBackPtr = true;
completeSplitFlags |= (1 << CSframe);
CSframe = (CSframe + 1) % 8;
}
break;
}
}
else
{
if (remainder == maxPacketSize)
continue;
USBLog(1, "AppleUSBEHCIHubInfo[%p]::AllocateIsochBandwidth: ran out of IN frames - need to redo", this);
}
}
if (remainder)
{
USBLog(1, "AppleUSBEHCIHubInfo[%p]::AllocateIsochBandwidth: not enough bandwidth for IN transaction", this);
return kIOReturnNoBandwidth;
}
}
for (frameIndex=0; (frameIndex < 8) && (framesUsed[frameIndex] < 255); frameIndex++)
{
USBLog(3, "AppleUSBEHCIHubInfo[%p]::AllocateIsochBandwidth IN: using %d bytes in frame %d", this, amountsUsed[frameIndex], framesUsed[frameIndex]);
pEP->bandwidthUsed[framesUsed[frameIndex]] = amountsUsed[frameIndex];
isochINUsed[framesUsed[frameIndex]] += amountsUsed[frameIndex];
}
pEP->startSplitFlags = startSplitFlags;
pEP->completeSplitFlags = completeSplitFlags;
break;
case kUSBOut:
USBLog(3, "AppleUSBEHCIHubInfo[%p]::AllocateIsochBandwidth: allocating %d bytes OUT", this, maxPacketSize);
for (i=0,frameIndex=0; i< 7; i++)
{
if ((interruptUsed[i] == 0) && (isochINUsed[i] == 0) && (isochOUTUsed[i] < kUSBEHCIMaxMicroframePeriodic))
{
USBLog(3, "AppleUSBEHCIHubInfo[%p]::AllocateIsochBandwidth: OUT frame %d already using %d", this, i, isochOUTUsed[i]);
if ((isochOUTUsed[i] + remainder) < kUSBEHCIMaxMicroframePeriodic)
thisFrameSize = remainder;
else
thisFrameSize = kUSBEHCIMaxMicroframePeriodic - isochOUTUsed[i];
USBLog(3, "AppleUSBEHCIHubInfo[%p]::AllocateIsochBandwidth: OUT frame %d will have %d bytes (remainder=%d)", this, i, thisFrameSize, remainder);
startSplitFlags |= (1 << i);
remainder -= thisFrameSize;
framesUsed[frameIndex] = i;
amountsUsed[frameIndex++] = thisFrameSize;
}
else
{
if (remainder == maxPacketSize)
continue;
USBLog(3, "AppleUSBEHCIHubInfo[%p]::AllocateIsochBandwidth: ran out of OUT frames - need to redo", this);
}
if (!remainder)
break;
}
if (remainder)
{
USBLog(1, "AppleUSBEHCIHubInfo[%p]::AllocateIsochBandwidth: not enough bandwidth for OUT transaction", this);
return kIOReturnNoBandwidth;
}
for (frameIndex=0; (frameIndex < 8) && (framesUsed[frameIndex] < 255); frameIndex++)
{
USBLog(3, "AppleUSBEHCIHubInfo[%p]::AllocateIsochBandwidth: using %d bytes in frame %d", this, amountsUsed[frameIndex], framesUsed[frameIndex]);
pEP->bandwidthUsed[framesUsed[frameIndex]] = amountsUsed[frameIndex];
isochOUTUsed[framesUsed[frameIndex]] += amountsUsed[frameIndex];
}
pEP->startSplitFlags = startSplitFlags;
pEP->completeSplitFlags = 0;
break;
default:
USBLog(1, "AppleUSBEHCIHubInfo[%p]::AllocateIsochBandwidth: unknown pEP direction (%d)", this, pEP->direction);
}
if (!remainder)
{
pEP->maxPacketSize = maxPacketSize;
USBLog(3, "AppleUSBEHCIHubInfo[%p]::AllocateIsochBandwidth: SS %x CS %x", this, pEP->startSplitFlags, pEP->completeSplitFlags);
return kIOReturnSuccess;
}
USBLog(1, "AppleUSBEHCIHubInfo[%p]::AllocateIsochBandwidth: returning kIOReturnNoBandwidth", this);
return kIOReturnNoBandwidth;
}
IOReturn
AppleUSBEHCIHubInfo::DeallocateIsochBandwidth(AppleEHCIIsochEndpointPtr pEP)
{
int i;
USBLog(3, "AppleUSBEHCIHubInfo[%p]::DeallocateIsochBandwidth: endpoint %p", this, pEP);
switch (pEP->direction)
{
case kUSBIn:
for (i=0;i<8;i++)
{
if (pEP->bandwidthUsed[i])
{
USBLog(3, "AppleUSBEHCIHubInfo[%p]::DeallocateIsochBandwidth: returning %d IN bytes, frame %d", this, pEP->bandwidthUsed[i], i);
isochINUsed[i] -= pEP->bandwidthUsed[i];
pEP->bandwidthUsed[i] = 0;
}
}
break;
case kUSBOut:
for (i=0;i<8;i++)
{
if (pEP->bandwidthUsed[i])
{
isochOUTUsed[i] -= pEP->bandwidthUsed[i];
USBLog(3, "AppleUSBEHCIHubInfo[%p]::DeallocateIsochBandwidth: returned %d OUT bytes, frame %d, isochOUTUsed now %d", this, pEP->bandwidthUsed[i], i, isochOUTUsed[i]);
pEP->bandwidthUsed[i] = 0;
}
}
break;
default:
USBLog(3, "AppleUSBEHCIHubInfo[%p]::DeallocateIsochBandwidth: unknown pEP direction (%d)", this, pEP->direction);
break;
}
pEP->maxPacketSize = 0;
return kIOReturnSuccess;
}
IOReturn
AppleUSBEHCIHubInfo::ReallocateIsochBandwidth(AppleEHCIIsochEndpointPtr pEP, UInt32 maxPacketSize)
{
IOReturn res = kIOReturnSuccess;
USBLog(3, "AppleUSBEHCIHubInfo[%p]::ReallocateIsochBandwidth: reallocating ep %p (%d) to size %d", this, pEP, pEP->maxPacketSize, maxPacketSize);
if (pEP->maxPacketSize != maxPacketSize)
{
if (pEP->maxPacketSize)
DeallocateIsochBandwidth(pEP);
res = AllocateIsochBandwidth(pEP, maxPacketSize);
}
else
USBLog(3, "AppleUSBEHCIHubInfo[%p]::ReallocateIsochBandwidth: MPS not changed, ignoring", this);
if (res)
USBLog(3, "AppleUSBEHCIHubInfo[%p]::ReallocateIsochBandwidth: reallocation failed, result=0x%x", this, res);
return res;
}