IOFireWireSBP2ORB.cpp [plain text]
#include <IOKit/sbp2/IOFireWireSBP2ORB.h>
#include <IOKit/sbp2/IOFireWireSBP2Login.h>
#include <IOKit/sbp2/IOFireWireSBP2LUN.h>
#include "FWDebugging.h"
#include <IOKit/firewire/IOFireWireController.h>
OSDefineMetaClassAndStructors( IOFireWireSBP2ORB, IOCommand );
OSMetaClassDefineReservedUnused(IOFireWireSBP2ORB, 0);
OSMetaClassDefineReservedUnused(IOFireWireSBP2ORB, 1);
OSMetaClassDefineReservedUnused(IOFireWireSBP2ORB, 2);
OSMetaClassDefineReservedUnused(IOFireWireSBP2ORB, 3);
OSMetaClassDefineReservedUnused(IOFireWireSBP2ORB, 4);
OSMetaClassDefineReservedUnused(IOFireWireSBP2ORB, 5);
OSMetaClassDefineReservedUnused(IOFireWireSBP2ORB, 6);
OSMetaClassDefineReservedUnused(IOFireWireSBP2ORB, 7);
OSMetaClassDefineReservedUnused(IOFireWireSBP2ORB, 8);
bool IOFireWireSBP2ORB::initWithLogin( IOFireWireSBP2Login * login )
{
bool res = true;
fLogin = login;
fLUN = getFireWireLUN();
fUnit = getFireWireUnit();
fControl = fUnit->getController();
fTimeoutTimerSet = false;
fRefCon = NULL;
fBufferAddressSpaceAllocated = false;
fBufferDescriptor = NULL;
fMaxPayloadSize = 0;
fCommandFlags = 0;
fTimeoutDuration = 0;
if( !IOCommand::init() )
return false;
IOReturn status = allocateResources();
if( status != kIOReturnSuccess )
res = false;
return res;
}
IOReturn IOFireWireSBP2ORB::allocateResources( void )
{
IOReturn status = kIOReturnSuccess;
if( status == kIOReturnSuccess )
{
UInt32 orbSize = sizeof(FWSBP2ORB) - 4 + fLogin->getMaxCommandBlockSize();
status = allocateORB( orbSize );
}
if( status == kIOReturnSuccess )
{
status = allocatePageTable( PAGE_SIZE / sizeof(FWSBP2PTE) ); }
if( status == kIOReturnSuccess )
{
status = allocateTimer();
}
if( status != kIOReturnSuccess )
{
deallocateTimer();
deallocateORB();
deallocatePageTable();
}
return status;
}
void IOFireWireSBP2ORB::release() const
{
if( getRetainCount() >= 2 )
IOCommand::release(2);
}
void IOFireWireSBP2ORB::free( void )
{
FWKLOG(( "IOFireWireSBP2ORB : free\n" ));
removeORB( this );
deallocateTimer();
deallocateORB();
deallocatePageTable();
deallocateBufferAddressSpace();
IOCommand::free();
}
IOReturn IOFireWireSBP2ORB::allocateORB( UInt32 orbSize )
{
IOReturn status = kIOReturnSuccess;
fORBDescriptor = IOBufferMemoryDescriptor::withOptions( kIODirectionOutIn | kIOMemoryUnshared, orbSize, orbSize );
if( fORBDescriptor == NULL )
status = kIOReturnNoMemory;
if( status == kIOReturnSuccess )
{
fORBDescriptor->setLength( orbSize );
IOPhysicalLength lengthOfSegment = 0;
IOPhysicalAddress phys = fORBDescriptor->getPhysicalSegment( 0, &lengthOfSegment );
FWKLOG( ( "IOFireWireSBP2ORB : ORB segment length = %d\n", lengthOfSegment ) );
if( lengthOfSegment != 0 )
{
fORBPhysicalAddress = phys;
FWKLOG( ( "IOFireWireSBP2ORB : ORB - phys = 0x%08lx\n", fORBPhysicalAddress ) );
}
else
{
status = kIOReturnNoMemory;
}
}
if( status == kIOReturnSuccess )
{
fORBBuffer = (FWSBP2ORB*)fORBDescriptor->getBytesNoCopy();
bzero( fORBBuffer, orbSize );
}
if( status == kIOReturnSuccess )
{
fORBPhysicalAddressSpace = fUnit->createPhysicalAddressSpace( fORBDescriptor );
if ( fORBPhysicalAddressSpace == NULL )
status = kIOReturnNoMemory;
}
if( status == kIOReturnSuccess )
{
status = fORBPhysicalAddressSpace->activate();
}
if( status == kIOReturnSuccess )
{
fORBPseudoAddressSpace = IOFWPseudoAddressSpace::simpleRW( fControl, &fORBPseudoAddress, fORBDescriptor );
if ( fORBPseudoAddressSpace == NULL )
status = kIOReturnNoMemory;
}
if( status == kIOReturnSuccess )
{
fORBPseudoAddress.addressHi &= 0xffff; status = fORBPseudoAddressSpace->activate();
FWKLOG( ( "IOFireWireSBP2ORB : created orb at phys: 0x%04lx.%08lx psuedo: 0x%04lx.%08lx of size %d\n", 0, fORBPhysicalAddress, fORBPseudoAddress.addressHi, fORBPseudoAddress.addressLo, orbSize ) );
}
if( status != kIOReturnSuccess )
{
deallocateORB();
}
return status;
}
void IOFireWireSBP2ORB::deallocateORB( void )
{
IOReturn status = kIOReturnSuccess;
if( fORBPhysicalAddressSpace != NULL && status == kIOReturnSuccess )
{
fORBPhysicalAddressSpace->deactivate();
fORBPhysicalAddressSpace->release();
fORBPhysicalAddressSpace = NULL;
}
if( fORBPseudoAddressSpace != NULL && status == kIOReturnSuccess )
{
fORBPseudoAddressSpace->deactivate();
fORBPseudoAddressSpace->release();
fORBPseudoAddressSpace = NULL;
}
if( fORBDescriptor != NULL )
{
fORBDescriptor->release();
fORBDescriptor = NULL;
}
}
void IOFireWireSBP2ORB::prepareORBForExecution( void )
{
fORBBuffer->nextORBAddressHi = 0x80000000;
fORBBuffer->nextORBAddressLo = 0x00000000;
UInt32 generation; UInt16 unitNode;
UInt16 localNode;
fUnit->getNodeIDGeneration( generation, unitNode, localNode );
fORBBuffer->dataDescriptorHi &= 0x0000ffff;
fORBBuffer->dataDescriptorHi |= ((UInt32)localNode) << 16;
fORBBuffer->options &= 0x000f;
if( fCommandFlags & kFWSBP2CommandCompleteNotify )
fORBBuffer->options |= 0x8000;
if( fCommandFlags & kFWSBP2CommandReservedORB )
fORBBuffer->options |= 0x2000;
if( fCommandFlags & kFWSBP2CommandVendorORB )
fORBBuffer->options |= 0x4000;
if( fCommandFlags & kFWSBP2CommandDummyORB )
fORBBuffer->options |= 0x6000;
if( fCommandFlags & kFWSBP2CommandTransferDataFromTarget )
fORBBuffer->options |= 0x0800;
IOFWSpeed speed = fUnit->FWSpeed();
switch( speed )
{
case kFWSpeed400MBit:
fORBBuffer->options |= 0x0200;
break;
case kFWSpeed200MBit:
fORBBuffer->options |= 0x0100;
break;
default:
break;
}
UInt32 transferSizeBytes = 2048;
UInt32 loginMaxPayloadSize = fLogin->getMaxPayloadSize();
if( loginMaxPayloadSize != 0 && loginMaxPayloadSize < transferSizeBytes )
transferSizeBytes = loginMaxPayloadSize;
if( fMaxPayloadSize != 0 && fMaxPayloadSize < transferSizeBytes )
transferSizeBytes = fMaxPayloadSize;
UInt32 transferSizeLog = 0;
while( (transferSizeBytes >= 8) && (transferSizeLog < 15) )
{
transferSizeBytes >>= 1;
transferSizeLog++;
}
UInt32 maxPackLog = fUnit->maxPackLog(!(fCommandFlags & kFWSBP2CommandTransferDataFromTarget));
maxPackLog -= 2; if( maxPackLog < transferSizeLog )
transferSizeLog = maxPackLog;
fORBBuffer->options |= transferSizeLog << 4;
}
IOReturn IOFireWireSBP2ORB::allocateTimer( void )
{
IOReturn status = kIOReturnSuccess;
if( status == kIOReturnSuccess )
{
fTimeoutCommand = (IOFWDelayCommand*)fControl->createDelayedCmd( fTimeoutDuration * 1000,
IOFireWireSBP2ORB::orbTimeoutStatic,
this );
if( fTimeoutCommand == NULL )
status = kIOReturnError;
}
if( status != kIOReturnSuccess )
{
deallocateTimer();
}
return status;
}
void IOFireWireSBP2ORB::deallocateTimer( void )
{
if( fTimeoutTimerSet )
cancelTimer();
if( fTimeoutCommand )
{
fTimeoutCommand->release();
fTimeoutCommand = NULL;
}
}
void IOFireWireSBP2ORB::startTimer( void )
{
if( fCommandFlags & kFWSBP2CommandCompleteNotify )
{
fInProgress = true;
if( fTimeoutDuration != 0 )
{
IOReturn ORBtimeoutSubmitStatus;
FWKLOG( ( "IOFireWireSBP2ORB : set timeout\n" ) );
fTimeoutTimerSet = true;
ORBtimeoutSubmitStatus = fTimeoutCommand->submit();
FWPANICASSERT( ORBtimeoutSubmitStatus == kIOReturnSuccess );
}
}
}
bool IOFireWireSBP2ORB::isTimerSet( void )
{
return fInProgress;
}
void IOFireWireSBP2ORB::cancelTimer( void )
{
FWKLOG( ( "IOFireWireSBP2ORB : cancel timer\n" ) );
if( fTimeoutTimerSet )
{
fTimeoutCommand->cancel( kIOReturnAborted );
}
else
{
fInProgress = false;
}
}
void IOFireWireSBP2ORB::orbTimeoutStatic( void *refcon, IOReturn status, IOFireWireBus *bus, IOFWBusCommand *fwCmd )
{
((IOFireWireSBP2ORB*)refcon)->orbTimeout( status, bus, fwCmd );
}
void IOFireWireSBP2ORB::orbTimeout( IOReturn status, IOFireWireBus *bus, IOFWBusCommand *fwCmd )
{
fTimeoutTimerSet = false;
fInProgress = false;
if( status == kIOReturnTimeout )
{
FWKLOG( ( "IOFireWireSBP2ORB : orb timeout\n" ) );
sendTimeoutNotification( this );
}
else
{
FWKLOG( ( "IOFireWireSBP2ORB : orb timeout cancelled\n" ) );
}
}
IOReturn IOFireWireSBP2ORB::allocatePageTable( UInt32 entryCount )
{
IOReturn status = kIOReturnSuccess;
deallocatePageTable();
fPageTableSize = sizeof(FWSBP2PTE) * entryCount;
fPageTableDescriptor = IOBufferMemoryDescriptor::withOptions( kIODirectionOutIn | kIOMemoryUnshared, fPageTableSize, PAGE_SIZE );
if( fPageTableDescriptor == NULL )
status = kIOReturnNoMemory;
if( status == kIOReturnSuccess )
{
fPageTableDescriptor->setLength( fPageTableSize );
IOPhysicalLength lengthOfSegment = 0;
IOPhysicalAddress phys = fPageTableDescriptor->getPhysicalSegment( 0, &lengthOfSegment );
if( lengthOfSegment != 0 )
{
fPageTablePhysicalAddress = phys;
}
else
{
status = kIOReturnNoMemory;
}
}
if( status == kIOReturnSuccess )
{
fPageTablePhysicalAddressSpace = fUnit->createPhysicalAddressSpace( fPageTableDescriptor );
if ( fPageTablePhysicalAddressSpace == NULL )
status = kIOReturnNoMemory;
}
if( status == kIOReturnSuccess )
{
status = fPageTablePhysicalAddressSpace->activate();
}
if( status == kIOReturnSuccess )
{
fPageTablePseudoAddressSpace = IOFWPseudoAddressSpace::simpleRW( fControl, &fPageTablePseudoAddress, fPageTableDescriptor );
if ( fPageTablePseudoAddressSpace == NULL )
status = kIOReturnNoMemory;
}
if( status == kIOReturnSuccess )
{
status = fPageTablePseudoAddressSpace->activate();
}
if( status != kIOReturnSuccess )
{
deallocatePageTable();
}
return status;
}
void IOFireWireSBP2ORB::deallocatePageTable( void )
{
IOReturn status = kIOReturnSuccess;
if( fPageTablePhysicalAddressSpace != NULL && status == kIOReturnSuccess )
{
fPageTablePhysicalAddressSpace->deactivate();
fPageTablePhysicalAddressSpace->release();
fPageTablePhysicalAddressSpace = NULL;
}
if( fPageTablePseudoAddressSpace != NULL && status == kIOReturnSuccess )
{
fPageTablePseudoAddressSpace->deactivate();
fPageTablePseudoAddressSpace->release();
fPageTablePseudoAddressSpace = NULL;
}
if( fPageTableDescriptor != NULL )
{
fPageTableDescriptor->release();
fPageTableDescriptor = NULL;
}
fPageTableSize = 0;
}
void IOFireWireSBP2ORB::deallocateBufferAddressSpace( void )
{
fControl->closeGate();
if( fBufferAddressSpaceAllocated )
{
fBufferAddressSpace->deactivate();
fBufferAddressSpace->release();
fBufferDescriptor->release();
fBufferAddressSpaceAllocated = false;
}
fControl->openGate();
}
IOReturn IOFireWireSBP2ORB::setCommandBuffersAsRanges( IOVirtualRange * ranges,
UInt32 withCount,
IODirection withDirection,
task_t withTask,
UInt32 offset,
UInt32 length )
{
IOReturn status = kIOReturnSuccess;
IOMemoryDescriptor * memory = NULL;
if( status == kIOReturnSuccess )
{
memory = IOMemoryDescriptor::withRanges( ranges, withCount, withDirection, withTask );
if( !memory )
status = kIOReturnNoMemory;
}
if( status == kIOReturnSuccess )
{
status = memory->prepare( withDirection );
}
if( status == kIOReturnSuccess )
{
status = setCommandBuffers( memory, offset, length );
}
if( memory )
{
memory->release();
}
return status;
}
IOReturn IOFireWireSBP2ORB::releaseCommandBuffers( void )
{
return setCommandBuffers( NULL );
}
IOReturn IOFireWireSBP2ORB::setCommandBuffers( IOMemoryDescriptor * memoryDescriptor, UInt32 offset, UInt32 length )
{
IOReturn status = kIOReturnSuccess;
fControl->closeGate();
UInt32 pte = 0;
vm_size_t pos = offset;
UInt32 step = 0;
UInt32 toMap = 0;
IOPhysicalAddress phys;
IOPhysicalLength lengthOfSegment;
if( memoryDescriptor != NULL )
{
if( length == 0 )
length = memoryDescriptor->getLength();
#if FWLOGGING
UInt32 tempLength = memoryDescriptor->getLength();
if(length != tempLength)
FWKLOG( ( "IOFireWireSBP2ORB : ### buffer length = %d, memDescriptor length = %d ###\n", length, tempLength ) );
#endif
while( pos < (length + offset) )
{
phys = memoryDescriptor->getPhysicalSegment(pos, &lengthOfSegment);
if( phys == 0 )
{
status = kIOReturnBadArgument; break;
}
while( (pos < (length + offset)) && (lengthOfSegment != 0) )
{
if( lengthOfSegment > kFWSBP2MaxPageClusterSize )
step = kFWSBP2MaxPageClusterSize;
else
step = lengthOfSegment;
lengthOfSegment -= step;
if( (pos + step) > (length + offset) )
toMap = length + offset - pos;
else
toMap = step;
if( toMap != 0 )
pte++;
pos += toMap;
}
}
FWKLOG( ( "IOFireWireSBP2ORB : number of required PTE's = %d\n", pte ) );
if( pte > fPageTableSize / sizeof(FWSBP2PTE) )
{
if( fCommandFlags & kFWSBP2CommandFixedSize )
{
fControl->openGate();
return kIOReturnNoMemory;
}
else
{
status = allocatePageTable( pte );
if( status != kIOReturnSuccess )
{
fControl->openGate();
return kIOReturnNoMemory;
}
}
}
}
deallocateBufferAddressSpace();
if( ( status == kIOReturnSuccess ) && ( memoryDescriptor != NULL ) )
{
pos = offset;
pte = 0;
while( pos < (length + offset) )
{
phys = memoryDescriptor->getPhysicalSegment(pos, &lengthOfSegment);
if( phys == 0 )
{
status = kIOReturnBadArgument; break;
}
while( (pos < (length + offset)) && (lengthOfSegment != 0) )
{
if( lengthOfSegment > kFWSBP2MaxPageClusterSize )
step = kFWSBP2MaxPageClusterSize;
else
step = lengthOfSegment;
lengthOfSegment -= step;
if( (pos + step) > (length + offset) )
toMap = length + offset - pos;
else
toMap = step;
if( toMap != 0 )
{
FWSBP2PTE entry;
entry.segmentLength = toMap;
entry.segmentBaseAddressHi = 0x0000;
entry.segmentBaseAddressLo = phys;
fPageTableDescriptor->writeBytes( pte * sizeof(FWSBP2PTE), &entry, sizeof(FWSBP2PTE) );
pte++;
phys += toMap;
pos += toMap;
}
}
}
if( status == kIOReturnSuccess )
{
fBufferDescriptor = memoryDescriptor;
fBufferDescriptor->retain();
fBufferAddressSpace = fUnit->createPhysicalAddressSpace( memoryDescriptor );
if( fBufferAddressSpace == NULL )
status = kIOReturnNoMemory;
}
if( status == kIOReturnSuccess )
{
status = fBufferAddressSpace->activate();
}
if( status == kIOReturnSuccess )
{
fBufferAddressSpaceAllocated = true;
}
}
if( status == kIOReturnSuccess )
{
if( pte == 0 )
{
fORBBuffer->dataDescriptorHi = 0;
fORBBuffer->dataDescriptorLo = 0;
fORBBuffer->options &= 0xfff0; fORBBuffer->dataSize = 0;
}
else
{
FWSBP2PTE firstEntry;
fPageTableDescriptor->readBytes( 0, &firstEntry, sizeof(FWSBP2PTE) );
if( pte == 1 && !(firstEntry.segmentBaseAddressLo & 0x00000003) ) {
fORBBuffer->dataDescriptorHi = 0; fORBBuffer->dataDescriptorLo = firstEntry.segmentBaseAddressLo;
fORBBuffer->options &= 0xfff0; fORBBuffer->dataSize = firstEntry.segmentLength;
}
else if( pte * sizeof(FWSBP2PTE) < PAGE_SIZE )
{
fORBBuffer->dataDescriptorHi = 0; fORBBuffer->dataDescriptorLo = fPageTablePhysicalAddress;
fORBBuffer->options &= 0xfff0;
fORBBuffer->options |= 0x0008; fORBBuffer->dataSize = pte;
}
else
{
fORBBuffer->dataDescriptorHi = fPageTablePseudoAddress.addressHi; fORBBuffer->dataDescriptorLo = fPageTablePseudoAddress.addressLo;
fORBBuffer->options &= 0xfff0;
fORBBuffer->options |= 0x0008; fORBBuffer->dataSize = pte;
}
}
}
fControl->openGate();
return status;
}
IOMemoryDescriptor * IOFireWireSBP2ORB::getCommandBufferDescriptor( void )
{
return fBufferDescriptor;
}
IOReturn IOFireWireSBP2ORB::setCommandBlock( void * buffer, UInt32 length )
{
fControl->closeGate();
UInt32 maxCommandBlockSize = fLogin->getMaxCommandBlockSize();
if( length > maxCommandBlockSize )
return kIOReturnNoMemory;
bzero( &fORBBuffer->commandBlock[0], maxCommandBlockSize );
bcopy( buffer, &fORBBuffer->commandBlock[0], length );
fControl->openGate();
return kIOReturnSuccess;
}
IOReturn IOFireWireSBP2ORB::setCommandBlock( IOMemoryDescriptor * memory )
{
fControl->closeGate();
UInt32 maxCommandBlockSize = fLogin->getMaxCommandBlockSize();
IOByteCount length = memory->getLength();
if( length > maxCommandBlockSize )
return kIOReturnNoMemory;
bzero( &fORBBuffer->commandBlock[0], maxCommandBlockSize );
memory->readBytes(0, &fORBBuffer->commandBlock[0], length );
fControl->openGate();
return kIOReturnSuccess;
}
void IOFireWireSBP2ORB::setCommandFlags( UInt32 flags )
{
fCommandFlags = flags;
}
UInt32 IOFireWireSBP2ORB::getCommandFlags( void )
{
return fCommandFlags;
}
void IOFireWireSBP2ORB::setRefCon( void * refCon )
{
fRefCon = refCon;
}
void * IOFireWireSBP2ORB::getRefCon( void )
{
return fRefCon;
}
void IOFireWireSBP2ORB::setMaxPayloadSize( UInt32 maxPayloadSize )
{
fMaxPayloadSize = maxPayloadSize;
}
UInt32 IOFireWireSBP2ORB::getMaxPayloadSize( void )
{
return fMaxPayloadSize;
}
void IOFireWireSBP2ORB::setCommandTimeout( UInt32 timeout )
{
IOReturn ORBTimeoutReinitStatus;
fControl->closeGate();
fTimeoutDuration = timeout;
ORBTimeoutReinitStatus = fTimeoutCommand->reinit( fTimeoutDuration * 1000, IOFireWireSBP2ORB::orbTimeoutStatic, this );
FWPANICASSERT( ORBTimeoutReinitStatus == kIOReturnSuccess );
fControl->openGate();
}
UInt32 IOFireWireSBP2ORB::getCommandTimeout( void )
{
return fTimeoutDuration;
}
void IOFireWireSBP2ORB::setCommandGeneration( UInt32 gen )
{
fGeneration = gen;
}
UInt32 IOFireWireSBP2ORB::getCommandGeneration( void )
{
return fGeneration;
}
void IOFireWireSBP2ORB::getORBAddress( FWAddress * address )
{
if( fCommandFlags & kFWSBP2CommandVirtualORBs )
*address = fORBPseudoAddress;
else
{
address->addressHi = 0L;
address->addressLo = fORBPhysicalAddress;
}
}
void IOFireWireSBP2ORB::setNextORBAddress( FWAddress address )
{
fORBBuffer->nextORBAddressHi = address.addressHi;
fORBBuffer->nextORBAddressLo = address.addressLo;
}
IOFireWireSBP2Login * IOFireWireSBP2ORB::getLogin( void )
{
return fLogin;
}
void IOFireWireSBP2ORB::setToDummy( void )
{
fORBBuffer->options |= 0x6000;
}
bool IOFireWireSBP2ORB::isAppended( void )
{
return fIsAppended;
}
void IOFireWireSBP2ORB::setIsAppended( bool state )
{
fIsAppended = state;
}
UInt32 IOFireWireSBP2ORB::getFetchAgentWriteRetries( void )
{
return fFetchAgentWriteRetries;
}
void IOFireWireSBP2ORB::setFetchAgentWriteRetries( UInt32 retries )
{
fFetchAgentWriteRetries = retries;
}
IOFireWireUnit * IOFireWireSBP2ORB::getFireWireUnit( void )
{
return fLogin->getFireWireUnit();
}
IOFireWireSBP2LUN * IOFireWireSBP2ORB::getFireWireLUN( void )
{
return fLogin->getFireWireLUN();
}
IOReturn IOFireWireSBP2ORB::removeORB( IOFireWireSBP2ORB * orb )
{
return fLogin->removeORB( orb );
}
void IOFireWireSBP2ORB::sendTimeoutNotification( IOFireWireSBP2ORB * orb )
{
fLogin->sendTimeoutNotification( orb );
}