#include <IOKit/firewire/IOFireWireFamilyCommon.h>
#include "IOFireWireIRM.h"
#include "FWDebugging.h"
#define kChannel31Mask 0x00000001
OSDefineMetaClassAndStructors( IOFireWireIRM, OSObject )
IOFireWireIRM * IOFireWireIRM::create( IOFireWireController * controller )
{
IOReturn status = kIOReturnSuccess;
IOFireWireIRM * me;
if( status == kIOReturnSuccess )
{
me = OSTypeAlloc( IOFireWireIRM );
if( me == NULL )
status = kIOReturnNoMemory;
}
if( status == kIOReturnSuccess )
{
bool success = me->initWithController( controller );
if( !success )
{
status = kIOReturnError;
}
}
if( status != kIOReturnSuccess )
{
me = NULL;
}
FWLOCALKLOG(( "IOFireWireIRM::create() - created new IRM 0x%08lx\n", (UInt32)me ));
return me;
}
bool IOFireWireIRM::initWithController(IOFireWireController * control)
{
IOReturn status = kIOReturnSuccess;
bool success = OSObject::init();
FWPANICASSERT( success == true );
fControl = control;
fIRMNodeID = kFWBadNodeID;
fOurNodeID = kFWBadNodeID;
fGeneration = 0;
fBroadcastChannelBuffer = OSSwapHostToBigInt32( kBroadcastChannelInitialValues );
fBroadcastChannelAddressSpace = IOFWPseudoAddressSpace::simpleRWFixed( fControl, FWAddress(kCSRRegisterSpaceBaseAddressHi, kCSRBroadcastChannel),
sizeof(fBroadcastChannelBuffer), &fBroadcastChannelBuffer );
FWPANICASSERT( fBroadcastChannelAddressSpace != NULL );
status = fBroadcastChannelAddressSpace->activate();
FWPANICASSERT( status == kIOReturnSuccess );
fLockCmdInUse = false;
fLockCmd = OSTypeAlloc( IOFWCompareAndSwapCommand );
FWPANICASSERT( fLockCmd != NULL );
fLockCmd->initAll( fControl, 0, FWAddress(), NULL, NULL, 0, IOFireWireIRM::lockCompleteStatic, this );
FWLOCALKLOG(( "IOFireWireIRM::initWithController() - IRM intialized\n" ));
return true;
}
void IOFireWireIRM::free()
{
FWLOCALKLOG(( "IOFireWireIRM::free() - freeing IRM 0x%08lx\n", (UInt32)this ));
if( fLockCmd != NULL )
{
if( fLockCmdInUse )
{
fLockCmd->cancel( kIOFireWireBusReset );
}
fLockCmd->release();
fLockCmd = NULL;
}
if( fBroadcastChannelAddressSpace != NULL)
{
fBroadcastChannelAddressSpace->deactivate();
fBroadcastChannelAddressSpace->release();
fBroadcastChannelAddressSpace = NULL;
}
OSObject::free();
}
bool IOFireWireIRM::isIRMActive( void )
{
return (fOurNodeID == fIRMNodeID && (fOurNodeID & 0x3f) != 0);
}
void IOFireWireIRM::processBusReset( UInt16 ourNodeID, UInt16 irmNodeID, UInt32 generation )
{
FWLOCALKLOG(( "IOFireWireIRM::processBusReset() - bus reset occurred\n" ));
FWLOCALKLOG(( "IOFireWireIRM::processBusReset() - ourNodeID = 0x%04x, irmNodeID = 0x%04x, generation = %d\n", ourNodeID, irmNodeID, generation ));
fIRMNodeID = irmNodeID;
fOurNodeID = ourNodeID;
fGeneration = generation;
if( isIRMActive() )
{
if( fLockCmdInUse )
{
fLockCmd->cancel( kIOFireWireBusReset );
}
fLockRetries = 8;
fOldChannelsAvailable31_0 = OSSwapHostToBigInt32( 0xffffffff );
allocateBroadcastChannel();
}
else
{
FWLOCALKLOG(( "IOFireWireIRM::processBusReset() - clear valid bit in BROADCAST_CHANNEL register\n" ));
fBroadcastChannelBuffer = OSSwapHostToBigInt32(kBroadcastChannelInitialValues);
}
}
void IOFireWireIRM::allocateBroadcastChannel( void )
{
IOReturn status = kIOReturnSuccess;
FWLOCALKLOG(( "IOFireWireIRM::allocateBroadcastChannel() - attempting to allocate broadcast channel\n" ));
FWAddress address( kCSRRegisterSpaceBaseAddressHi, kCSRChannelsAvailable31_0 );
address.nodeID = fIRMNodeID;
UInt32 host_channels_available = OSSwapBigToHostInt32( fOldChannelsAvailable31_0 );
host_channels_available &= ~kChannel31Mask;
fNewChannelsAvailable31_0 = OSSwapHostToBigInt32( host_channels_available );
fLockCmd->reinit( fGeneration, address, &fOldChannelsAvailable31_0, &fNewChannelsAvailable31_0, 1, IOFireWireIRM::lockCompleteStatic, this );
fLockCmdInUse = true;
status = fLockCmd->submit();
}
void IOFireWireIRM::lockCompleteStatic( void *refcon, IOReturn status, IOFireWireNub *device, IOFWCommand *fwCmd )
{
IOFireWireIRM * me = (IOFireWireIRM*)refcon;
me->lockComplete( status );
}
void IOFireWireIRM::lockComplete( IOReturn status )
{
bool done = true;
fLockCmdInUse = false;
if( status == kIOReturnSuccess )
{
bool tryAgain = !fLockCmd->locked( &fOldChannelsAvailable31_0 );
if( tryAgain && fLockRetries-- )
{
FWLOCALKLOG(( "IOFireWireIRM::lockComplete() - allocation attempt failed, will retry\n" ));
allocateBroadcastChannel();
done = false;
}
}
if( done )
{
#if FWLOCALLOGGING
if( status == kIOReturnSuccess )
{
FWLOCALKLOG(( "IOFireWireIRM::lockComplete() - successfully allocated broadcast channel\n" ));
}
else
{
FWLOCALKLOG(( "IOFireWireIRM::lockComplete() - failed to allocate broadcast channel\n" ));
}
#endif
if( status != kIOFireWireBusReset )
{
FWLOCALKLOG(( "IOFireWireIRM::lockComplete() - set valid bit in BROADCAST_CHANNEL register\n" ));
fBroadcastChannelBuffer = OSSwapHostToBigInt32( kBroadcastChannelInitialValues | kBroadcastChannelValidMask );
}
}
}
#pragma mark -
OSDefineMetaClassAndStructors( IOFireWireIRMAllocation, OSObject );
OSMetaClassDefineReservedUnused(IOFireWireIRMAllocation, 0);
OSMetaClassDefineReservedUnused(IOFireWireIRMAllocation, 1);
OSMetaClassDefineReservedUnused(IOFireWireIRMAllocation, 2);
OSMetaClassDefineReservedUnused(IOFireWireIRMAllocation, 3);
OSMetaClassDefineReservedUnused(IOFireWireIRMAllocation, 4);
OSMetaClassDefineReservedUnused(IOFireWireIRMAllocation, 5);
OSMetaClassDefineReservedUnused(IOFireWireIRMAllocation, 6);
OSMetaClassDefineReservedUnused(IOFireWireIRMAllocation, 7);
struct IRMAllocationThreadInfo
{
IOFireWireIRMAllocation * fIRMAllocation;
UInt32 fGeneration;
IOFireWireController * fControl;
IORecursiveLock * fLock;
UInt8 fIsochChannel;
UInt32 fBandwidthUnits;
};
bool IOFireWireIRMAllocation::init( IOFireWireController * control,
Boolean releaseIRMResourcesOnFree,
AllocationLostNotificationProc allocationLostProc,
void *pLostProcRefCon)
{
if (!OSObject::init())
return false ;
fLock = IORecursiveLockAlloc () ;
if ( ! fLock )
return false ;
fControl = control;
fAllocationGeneration = 0xFFFFFFFF;
fAllocationLostProc = allocationLostProc;
fLostProcRefCon = pLostProcRefCon;
fReleaseIRMResourcesOnFree = releaseIRMResourcesOnFree;
fBandwidthUnits = 0;
fIsochChannel = 64;
fControl->addToIRMAllocationSet(this);
isAllocated = false;
return true;
}
void IOFireWireIRMAllocation::release() const
{
DebugLog( "IOFireWireIRMAllocation::release, retain count before release = %d\n",getRetainCount() ) ;
IORecursiveLockLock(fLock);
int retainCnt = getRetainCount();
if ( retainCnt == 2 )
{
if( isAllocated == false )
{
fControl->removeFromIRMAllocationSet((IOFireWireIRMAllocation*)this);
}
else
{
fControl->removeIRMAllocation((IOFireWireIRMAllocation*)this);
}
}
OSObject::release();
if (retainCnt != 1)
IORecursiveLockUnlock(fLock);
}
void IOFireWireIRMAllocation::free( void )
{
DebugLog( "IOFireWireIRMAllocation::free\n") ;
IORecursiveLockLock(fLock);
if (isAllocated)
{
if (fReleaseIRMResourcesOnFree)
{
if (fBandwidthUnits > 0)
fControl->releaseIRMBandwidthInGeneration(fBandwidthUnits,fAllocationGeneration);
if (fIsochChannel < 64)
fControl->releaseIRMChannelInGeneration(fIsochChannel,fAllocationGeneration);
}
}
if ( fLock )
IORecursiveLockFree( fLock ) ;
OSObject::free();
}
IOReturn IOFireWireIRMAllocation::allocateIsochResources(UInt8 isochChannel, UInt32 bandwidthUnits)
{
IOReturn res = kIOReturnError;
UInt32 irmGeneration;
UInt16 irmNodeID;
IORecursiveLockLock(fLock);
if (!isAllocated)
{
fAllocationGeneration = 0xFFFFFFFF;
fControl->getIRMNodeID(irmGeneration, irmNodeID);
res = kIOReturnSuccess;
if (isochChannel < 64)
{
res = fControl->allocateIRMChannelInGeneration(isochChannel,irmGeneration);
}
if ((res == kIOReturnSuccess) && (bandwidthUnits > 0))
{
res = fControl->allocateIRMBandwidthInGeneration(bandwidthUnits,irmGeneration);
if (res != kIOReturnSuccess)
{
fControl->releaseIRMChannelInGeneration(isochChannel,irmGeneration);
}
}
if (res == kIOReturnSuccess)
{
fIsochChannel = isochChannel;
fBandwidthUnits = bandwidthUnits;
fAllocationGeneration = irmGeneration;
isAllocated = true;
fControl->addIRMAllocation(this);
}
}
IORecursiveLockUnlock(fLock);
FWTrace( kFWTIsoch, kTPIsochIRMAllocateIsochResources, (uintptr_t)(fControl->getLink()), fIsochChannel, fBandwidthUnits, res );
return res;
}
IOReturn IOFireWireIRMAllocation::deallocateIsochResources(void)
{
IOReturn res = kIOReturnError;
IORecursiveLockLock(fLock);
if (isAllocated)
{
if (fBandwidthUnits > 0)
fControl->releaseIRMBandwidthInGeneration(fBandwidthUnits,fAllocationGeneration);
if (fIsochChannel < 64)
fControl->releaseIRMChannelInGeneration(fIsochChannel,fAllocationGeneration);
fControl->removeIRMAllocation(this);
isAllocated = false;
fBandwidthUnits = 0;
fIsochChannel = 64;
fAllocationGeneration = 0xFFFFFFFF;
}
IORecursiveLockUnlock(fLock);
return res;
}
Boolean IOFireWireIRMAllocation::areIsochResourcesAllocated(UInt8 *pAllocatedIsochChannel, UInt32 *pAllocatedBandwidthUnits)
{
*pAllocatedIsochChannel = fIsochChannel;
*pAllocatedBandwidthUnits = fBandwidthUnits;
return isAllocated;
}
void * IOFireWireIRMAllocation::GetRefCon(void)
{
return fLostProcRefCon;
}
void IOFireWireIRMAllocation::SetRefCon(void* refCon)
{
fLostProcRefCon = refCon;
}
void IOFireWireIRMAllocation::handleBusReset(UInt32 generation)
{
IORecursiveLockLock(fLock);
if (!isAllocated)
{
IORecursiveLockUnlock(fLock);
return;
}
if (fAllocationGeneration == generation)
{
IORecursiveLockUnlock(fLock);
return;
}
IRMAllocationThreadInfo * threadInfo = (IRMAllocationThreadInfo *)IOMalloc( sizeof(IRMAllocationThreadInfo) );
if( threadInfo )
{
threadInfo->fGeneration = generation;
threadInfo->fIRMAllocation = this;
threadInfo->fControl = fControl;
threadInfo->fLock = fLock;
threadInfo->fIsochChannel = fIsochChannel;
threadInfo->fBandwidthUnits = fBandwidthUnits;
retain();
thread_t thread;
if( kernel_thread_start((thread_continue_t)threadFunc, threadInfo, &thread ) == KERN_SUCCESS )
{
thread_deallocate(thread);
}
}
IORecursiveLockUnlock(fLock);
}
void IOFireWireIRMAllocation::setReleaseIRMResourcesOnFree(Boolean doRelease)
{
fReleaseIRMResourcesOnFree = doRelease;
}
UInt32 IOFireWireIRMAllocation::getAllocationGeneration(void)
{
return fAllocationGeneration;
}
void IOFireWireIRMAllocation::failedToRealloc(void)
{
if (fAllocationLostProc)
fAllocationLostProc(fLostProcRefCon,this);
fControl->removeIRMAllocation(this);
isAllocated = false;
fAllocationGeneration = 0xFFFFFFFF;
}
void IOFireWireIRMAllocation::threadFunc( void * arg )
{
IOReturn res = kIOReturnSuccess;
IRMAllocationThreadInfo * threadInfo = (IRMAllocationThreadInfo *)arg;
IOFireWireIRMAllocation *pIRMAllocation = threadInfo->fIRMAllocation;
IORecursiveLock * fLock = threadInfo->fLock;
UInt32 generation = threadInfo->fGeneration;
UInt32 irmGeneration;
UInt16 irmNodeID;
IORecursiveLockLock(fLock);
threadInfo->fControl->getIRMNodeID(irmGeneration, irmNodeID);
if ((irmGeneration == generation) && (pIRMAllocation->getAllocationGeneration() != 0xFFFFFFFF))
{
if (threadInfo->fIsochChannel < 64)
{
res = threadInfo->fControl->allocateIRMChannelInGeneration(threadInfo->fIsochChannel,generation);
}
if ((res == kIOReturnSuccess) && (threadInfo->fBandwidthUnits > 0))
{
res = threadInfo->fControl->allocateIRMBandwidthInGeneration(threadInfo->fBandwidthUnits,generation);
if (res != kIOReturnSuccess)
{
threadInfo->fControl->releaseIRMChannelInGeneration(threadInfo->fIsochChannel,generation);
}
}
if ((res != kIOReturnSuccess) && (res != kIOFireWireBusReset))
{
pIRMAllocation->failedToRealloc();
}
}
IORecursiveLockUnlock(fLock);
IOFree( threadInfo, sizeof(threadInfo) );
pIRMAllocation->release();
FWTrace( kFWTIsoch, kTPIsochIRMThreadFunc, (uintptr_t)(threadInfo->fControl->getLink()), threadInfo->fIsochChannel, threadInfo->fBandwidthUnits, res );
}