/* * Copyright (c) 1998-2001 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@ */ /* * IOFWUserLocalIsochPortProxy.cpp * IOFireWireFamily * * Created by NWG on Tue Mar 20 2001. * Copyright (c) 2001 Apple Computer, Inc. All rights reserved. * */ #include #include #include #include "IOFireWireUserClient.h" #include "IOFWUserIsochPort.h" #include /****************************************************************/ OSDefineMetaClassAndStructors(IOFWUserIsochPort, IOFWIsochPort) /****************************************************************/ // if we wanted super intergration with user space, we could have these functions // callout to user space syncronously with a failure on timeout.. but this // type of port is not currently called from inside the kernel. bool IOFWUserIsochPort::init() { return IOFWIsochPort::init() ; } IOReturn IOFWUserIsochPort::getSupported( IOFWSpeed& maxSpeed, UInt64& chanSupported) { IOFireWireUserClientLog_(("+ IOFWUserIsochPort::getSupported\n")) ; return kIOReturnUnsupported ; } IOReturn IOFWUserIsochPort::allocatePort(IOFWSpeed speed, UInt32 chan) { IOFireWireUserClientLog_(("+ IOFWUserIsochPort::allocatePort\n")) ; return kIOReturnSuccess ; } IOReturn IOFWUserIsochPort::releasePort() { IOFireWireUserClientLog_(("+ IOFWUserIsochPort::releasePort\n")) ; return kIOReturnSuccess ; } IOReturn IOFWUserIsochPort::start() { IOFireWireUserClientLog_(("+ IOFWUserIsochPort::start\n")) ; return kIOReturnSuccess ; } IOReturn IOFWUserIsochPort::stop() { IOFireWireUserClientLog_(("+ IOFWUserIsochPort::stop\n")) ; return kIOReturnSuccess ; } #pragma mark - #pragma mark --- IOFWUserIsochPortProxy ---------- /****************************************************************/ OSDefineMetaClassAndStructors(IOFWUserIsochPortProxy, OSObject) /****************************************************************/ Boolean IOFWUserIsochPortProxy::init( IOFireWireUserClient* inUserClient) { fUserClient = inUserClient ; fUserClient->retain() ; fPortStarted = false ; fPortAllocated = false ; // IOFWUserIsochPort* newPort = new IOFWUserIsochPort ; // if (newPort && !newPort->init()) // { // newPort->release() ; // newPort = NULL ; // // } // fPort = newPort ; fPort = NULL ; IOFireWireUserClientLog_(("IOFWUserIsochPortProxy::init: this=0x%08lX, fPort = %08lX\n", this, fPort)) ; // return (fPort != NULL) ; return true ; } IOReturn IOFWUserIsochPortProxy::getSupported(IOFWSpeed &maxSpeed, UInt64 &chanSupported) { IOReturn result = kIOReturnSuccess ; if (!fPort) result = createPort() ; if ( result == kIOReturnSuccess ) result = fPort->getSupported(maxSpeed, chanSupported) ; return result ; } IOReturn IOFWUserIsochPortProxy::allocatePort(IOFWSpeed speed, UInt32 chan) { IOReturn result = kIOReturnSuccess ; if (!fPortAllocated) { if (!fPort) result = createPort() ; if ( result == kIOReturnSuccess ) result = fPort->allocatePort(speed, chan) ; fPortAllocated = (kIOReturnSuccess == result) ; } return result ; } IOReturn IOFWUserIsochPortProxy::releasePort() { IOReturn result = kIOReturnSuccess ; if ( fPortStarted ) { IOFireWireUserClientLog_( ("IOFWUserIsochPortProxy::releasePort: calling releasePort() before stop()!\n" ) ) ; if (!fPort) IOLog("%s %u: strange: fPort == nil when emergency releasing port.\n", __FILE__, __LINE__) ; if ( kIOReturnSuccess != fPort->stop() ) IOLog("%s %u: fPort->stop() returned error\n", __FILE__, __LINE__) ; } if (fPortAllocated) { if (!fPort) { IOLog("%s %u: strange: fPort == nil when calling releasePort.\n", __FILE__, __LINE__) ; result = kIOReturnError ; } if ( result == kIOReturnSuccess ) result = fPort->releasePort() ; fPortAllocated = false ; } return result ; } IOReturn IOFWUserIsochPortProxy::start() { IOReturn result = kIOReturnSuccess ; if (fPortAllocated && !fPortStarted) { if (!fPort) result = createPort() ; if ( result == kIOReturnSuccess ) result = fPort->start() ; else IOLog("%s %u: IOFWUserIsochPortProxt::Start(): error %x trying to allocate isoch port\n", __FILE__, __LINE__, result ) ; IOFireWireUserClientLog_(("starting port 0x%08lX\n", this)) ; fPortStarted = (kIOReturnSuccess == result) ; } return result ; } IOReturn IOFWUserIsochPortProxy::stop() { IOReturn result = kIOReturnSuccess ; if (fPortStarted) { IOFireWireUserClientLog_(("IOFWUserIsochPortProxy::stop: stopping port 0x%08lX\n", fPort)) ; if (!fPort) { IOLog("%s %u: IOFWUserIsochPortProxy::stop(): fPort == nil!\n", __FILE__, __LINE__) ; result = kIOReturnError ; } if ( result == kIOReturnSuccess ) result = fPort->stop() ; fPortStarted = false ; } return result ; } void IOFWUserIsochPortProxy::free() { IOFireWireUserClientLog_(("IOFWUserIsochPortProxy::free: this=0x%08lX, releasing fPort @ 0x%08lX\n", this, fPort)) ; if (fPort) { fPort->release() ; fPort = NULL ; } if (fUserClient) { fUserClient->release() ; fUserClient = NULL ; } OSObject::free() ; } IOReturn IOFWUserIsochPortProxy::createPort() { IOReturn result = kIOReturnSuccess ; IOFWUserIsochPort* newPort = new IOFWUserIsochPort ; if (newPort && !newPort->init()) { newPort->release() ; newPort = NULL ; result = kIOReturnNoMemory ; } fPort = newPort ; return result ; } #pragma mark - #pragma mark --- IOFWUserLocalIsochPortProxy ---------- /****************************************************************/ OSDefineMetaClassAndStructors(IOFWUserLocalIsochPortProxy, IOFWUserIsochPortProxy) /****************************************************************/ Boolean IOFWUserLocalIsochPortProxy::initWithUserDCLProgram( FWLocalIsochPortAllocateParams* inParams, IOFireWireUserClient* inUserClient) { if (!IOFWUserIsochPortProxy::init(inUserClient)) return false ; // init for safety! // fUserDCLProgramMem = NULL ; // fDCLProgramBytes = 0 ; // fKernDCLProgramBuffer = NULL ; // fUserBufferMem = NULL ; // fUserBufferMemPrepared = false ; fTalking = inParams->talking ; fStartEvent = inParams->startEvent ; fStartState = inParams->startState ; fStartMask = inParams->startMask ; // // Note: The ranges passed in must be sorted. (Should be if user space code doesn't change) //zzz is this still true? // IOReturn result = kIOReturnSuccess ; IOByteCount tempLength = 0 ; // holds throw away value. // // Get user space range list into kernel and copy user space DCL program to a kernel buffer: // // allocate list for user space virtual ranges for user space DCL's IOVirtualRange* programRanges = NULL ; IOVirtualRange* bufferRanges = NULL ; // create a memory descriptor around ranges list in user space programRanges = new IOVirtualRange[ inParams->userDCLProgramRangeCount ] ; IOMemoryDescriptor* programRangesMem = IOMemoryDescriptor::withAddress((IOVirtualAddress)(inParams->userDCLProgramRanges), sizeof(IOVirtualRange) * inParams->userDCLProgramRangeCount, kIODirectionIn, fUserClient->getOwningTask() ) ; if (!programRangesMem) result = kIOReturnNoMemory ; // copy user space range list to in kernel list IOByteCount programStartOffset = 0 ; if ( kIOReturnSuccess == result ) result = programRangesMem->prepare() ; if ( kIOReturnSuccess == result ) { programRangesMem->readBytes(0 /*offset*/, programRanges, sizeof(IOVirtualRange) * inParams->userDCLProgramRangeCount) ; // gotcha! // we've copied the user's ranges list, so we're done with this memory descriptor. programRangesMem->complete() ; programRangesMem->release() ; if (!findOffsetInRanges( (IOVirtualAddress)(inParams->userDCLProgram), programRanges, inParams->userDCLProgramRangeCount, & programStartOffset)) { IOFireWireUserClientLog_(("IOFWUserLocalIsochPortProxy::initWithUserDCLProgram: failed to find program starting point in ranges provided")) ; } // use list to create a memory descriptor covering the memory containing the program in user space fUserDCLProgramMem = IOMemoryDescriptor::withRanges(programRanges, inParams->userDCLProgramRangeCount, kIODirectionOutIn, fUserClient->getOwningTask()) ; if (!fUserDCLProgramMem) result = kIOReturnNoMemory ; } if ( kIOReturnSuccess == result ) { result = fUserDCLProgramMem->prepare() ; fUserDCLProgramMemPrepared = ( result == kIOReturnSuccess ) ; } // allocate an in-kernel buffer to hold a copy of the DCL program from user space if (kIOReturnSuccess == result) { fDCLProgramBytes = fUserDCLProgramMem->getLength() ; if ( NULL == ( fKernDCLProgramBuffer = new UInt8[ fDCLProgramBytes ] ) ) result = kIOReturnNoMemory ; fKernDCLProgramStart = (DCLCommandStruct*)(fKernDCLProgramBuffer + programStartOffset) ; } // // === Map user data buffers into kernel // // create an array to hold list of user space ranges if ( kIOReturnSuccess == result ) { if ( NULL == ( bufferRanges = new IOVirtualRange[ inParams->userDCLBufferRangeCount ] ) ) result = kIOReturnNoMemory ; } // create memory descriptor to cover buffer ranges array in user space IOMemoryDescriptor* bufferRangesMem = 0 ; if (kIOReturnSuccess == result) { bufferRangesMem = IOMemoryDescriptor::withAddress((IOVirtualAddress)inParams->userDCLBufferRanges, inParams->userDCLBufferRangeCount * sizeof(IOVirtualRange), kIODirectionIn, fUserClient->getOwningTask() ) ; if (!bufferRangesMem) result = kIOReturnNoMemory ; } // copy user space range list to in-kernel list if ( kIOReturnSuccess == result ) result = bufferRangesMem->prepare() ; if (kIOReturnSuccess == result) { bufferRangesMem->readBytes(0, bufferRanges, bufferRangesMem->getLength()) ; // we're done with this bufferRangesMem->complete() ; } if ( bufferRangesMem ) { bufferRangesMem->release() ; } // convert ranges to be page aligned and have lengths on multiples of whole pages only // IOMemoryDescripor::map() will fail otherwise... for( UInt32 i=0; i < inParams->userDCLBufferRangeCount; i++ ) { if ( !page_aligned( bufferRanges[i].address ) ) { bufferRanges[i].length += bufferRanges[i].address - trunc_page( bufferRanges[i].address ) ; bufferRanges[i].address = trunc_page( bufferRanges[i].address ) ; } bufferRanges[i].length = round_page( bufferRanges[i].address + bufferRanges[i].length ) - bufferRanges[i].address ; } // create a memory descriptor to cover user buffers using list of virtual ranges // copied from user space if ( kIOReturnSuccess == result ) { fUserBufferMem = IOMemoryDescriptor::withRanges( bufferRanges, inParams->userDCLBufferRangeCount, kIODirectionOutIn, fUserClient->getOwningTask() ) ; if ( !fUserBufferMem ) result = kIOReturnNoMemory ; } if ( kIOReturnSuccess == result ) { result = fUserBufferMem->prepare( kIODirectionOutIn ) ; fUserBufferMemPrepared = ( kIOReturnSuccess == result ) ; } // allocate a buffer to hold the user space --> kernel dcl mapping table if (kIOReturnSuccess == result) { fUserToKernelDCLLookupTableLength = inParams->userDCLProgramDCLCount ; fUserToKernelDCLLookupTable = new DCLCommandStruct*[ fUserToKernelDCLLookupTableLength ] ; if ( !fUserToKernelDCLLookupTable ) result = kIOReturnNoMemory ; } DCLCommandStruct** userDCLTable ; // array if (kIOReturnSuccess == result) { userDCLTable = new (DCLCommandStruct*)[ fUserToKernelDCLLookupTableLength ] ; if (!userDCLTable) result = kIOReturnNoMemory ; } // // B. convert program pointers // if ( kIOReturnSuccess == result ) { // copy user DCL program to contiguous kernel DCL program buffer fUserDCLProgramMem->readBytes(0, fKernDCLProgramBuffer, fDCLProgramBytes) ; // walk the DCL program and // a. convert user next ptr's to kernel next ptr's // b. convert user data buffers to kernel data buffers // c. fill in table entry mapping user ref ID to kernel DCL fUserBufferMemMap = fUserBufferMem->map() ; if ( !fUserBufferMemMap ) { fKernDCLProgramStart = NULL ; result = kIOReturnVMError ; } if (fKernDCLProgramStart) { DCLCommandStruct* pCurrentKernDCL = fKernDCLProgramStart ; UInt32 lookupTableLength = 0 ; DCLCommandStruct* lastNextUserDCL = inParams->userDCLProgram ; IOByteCount lastDistance = programStartOffset ; while ( pCurrentKernDCL && (kIOReturnSuccess == result) ) { IOByteCount distance = 0 ; IOVirtualRange tempBufferRange ; // add this DCL to our lookup table userDCLTable[lookupTableLength] = lastNextUserDCL ; lastNextUserDCL = pCurrentKernDCL->pNextDCLCommand ; fUserToKernelDCLLookupTable[lookupTableLength] = pCurrentKernDCL ; // clear out any remnant garbage in compiler data field: pCurrentKernDCL->compilerData = 0 ; // if DCL has a data buffer, // convert user space data buffer pointer to in-kernel data pointer if (getDCLDataBuffer(pCurrentKernDCL, & tempBufferRange.address, & tempBufferRange.length)) { if (!findOffsetInRanges( tempBufferRange.address, bufferRanges, inParams->userDCLBufferRangeCount, & distance)) IOFireWireUserClientLog_(("IOFWUserLocalIsochPortProxy::initWithUserDCLProgram: couldn't find DCL data buffer in buffer ranges")) ; // set DCL's data pointer to point to same memory in kernel address space IOVirtualAddress baseAddr = fUserBufferMemMap->getVirtualAddress() ; setDCLDataBuffer( pCurrentKernDCL, baseAddr + distance, tempBufferRange.length ) ; } // check for other DCL command types that might need conversion switch( pCurrentKernDCL->opcode & ~kFWDCLOpFlagMask ) { case kDCLPtrTimeStampOp: result = convertToKernelDCL( (DCLPtrTimeStampStruct*) pCurrentKernDCL, bufferRanges, inParams->userDCLBufferRangeCount, fUserBufferMem) ; break ; case kDCLCallProcOp: result = convertToKernelDCL( (DCLCallProcStruct*) pCurrentKernDCL, userDCLTable[lookupTableLength] ) ; break ; default: break ; } // // convert user space next pointer to in-kernel next pointer // // since all the DCL's have been copied into the kernel sequentially, we can // walk the user program and keep track of how many bytes of DCL program we // pass through, then use that as an offset from the beginning of our // kernel DCL program buffer. We then set the current DCL's next pointer to point to // the proper in-kernel DCL lookupTableLength++ ; if ( pCurrentKernDCL->pNextDCLCommand && kIOReturnSuccess == result ) { if ( !findOffsetInRanges( (IOVirtualAddress)( pCurrentKernDCL->pNextDCLCommand ), programRanges, inParams->userDCLProgramRangeCount, & distance ) ) { IOFireWireUserClientLog_(("IOFWUserLocalIsochPortProxy::initWithUserDCLProgram: couldn't find DCL next ptr in program ranges")) ; } else pCurrentKernDCL->pNextDCLCommand = (DCLCommandStruct*)(fKernDCLProgramBuffer + distance) ; } // fill in compiler data in user space program to point to the proper lookup table entry fUserDCLProgramMem->writeBytes(lastDistance + ( (IOVirtualAddress)&pCurrentKernDCL->compilerData - (IOVirtualAddress)&pCurrentKernDCL->pNextDCLCommand ), & lookupTableLength /*dclIndex*/, sizeof(pCurrentKernDCL->compilerData)) ; lastDistance = distance ; // move to next DCL (next ptr has been converted to kernel next ptr) pCurrentKernDCL = pCurrentKernDCL->pNextDCLCommand ; } // second pass of translation. this converts dcls that need to refer to other, already translated DCLs in the program. // this includes, for example, jump and update list DCLs if ( kIOReturnSuccess == result ) { // reset parameters UInt32 hint = 0 ; pCurrentKernDCL = fKernDCLProgramStart ; while ( pCurrentKernDCL && (kIOReturnSuccess == result) ) { // check for other DCL command types that might need conversion switch( pCurrentKernDCL->opcode & ~kFWDCLOpFlagMask ) { case kDCLJumpOp: result = convertToKernelDCL( (DCLJumpStruct*) pCurrentKernDCL, userDCLTable, fUserToKernelDCLLookupTable, lookupTableLength, hint ) ; break ; case kDCLUpdateDCLListOp: result = convertToKernelDCL( (DCLUpdateDCLListStruct*)pCurrentKernDCL, userDCLTable, fUserToKernelDCLLookupTable, lookupTableLength, hint) ; break ; default: break ; } // move to next DCL (next ptr has been converted to kernel next ptr) pCurrentKernDCL = pCurrentKernDCL->pNextDCLCommand ; } } // warning: lots of output // printDCLProgram( fKernDCLProgramStart, inParams->userDCLProgramDCLCount ) ; } } // memory descriptor makes a copy, so we can delete these delete[] programRanges ; delete[] bufferRanges ; // delete temporary storage delete[] userDCLTable ; return (kIOReturnSuccess == result) ; } void IOFWUserLocalIsochPortProxy::free() { // should walk DCL program here and free all allocated associated data structures... if (fUserBufferMem) { if (fUserBufferMemPrepared) fUserBufferMem->complete() ; if ( fUserBufferMemMap ) fUserBufferMemMap->release() ; fUserBufferMem->release() ; } if ( fUserDCLProgramMem ) { if ( fUserDCLProgramMemPrepared ) fUserDCLProgramMem->complete() ; fUserDCLProgramMem->release() ; fUserDCLProgramMem = NULL ; } delete[] fUserToKernelDCLLookupTable ; if (fKernDCLProgramBuffer) { DCLCommandStruct* currentDCL = fKernDCLProgramStart ; while (currentDCL) { switch( currentDCL->opcode ) { case kDCLCallProcOp: // the procData field of this DCL can hold a pointer to an AsyncRefHolder structure, // dynamically allocated when the program was sent in from user space if (((DCLCallProcStruct*)currentDCL)->procData) delete (AsyncRefHolder*)(((DCLCallProcStruct*)currentDCL)->procData) ; break ; case kDCLUpdateDCLListOp: // we allocated an array to hold pointers to the DCLs that need updating. // we free the array here... delete[] ((DCLUpdateDCLListStruct*)currentDCL)->dclCommandList ; break ; default: // it don't mean a thing if it ain't got that swing... break ; } currentDCL = currentDCL->pNextDCLCommand ; } delete[] fKernDCLProgramBuffer ; } IOFWUserIsochPortProxy::free() ; } IOReturn IOFWUserLocalIsochPortProxy::getSupported(IOFWSpeed &maxSpeed, UInt64 &chanSupported) { if (!fPort) createPort() ; IOReturn result = IOFWUserIsochPortProxy::getSupported( maxSpeed, chanSupported ) ; return result ; } IOReturn IOFWUserLocalIsochPortProxy::allocatePort(IOFWSpeed speed, UInt32 chan) { IOReturn result = kIOReturnSuccess ; if (!fPort) result = createPort() ; if ( result == kIOReturnSuccess && !fUserBufferMemPrepared ) { result = fUserBufferMem->prepare() ; fUserBufferMemPrepared = ( result == kIOReturnSuccess ) ; } // if ( result == kIOReturnSuccess ) // { // fUserBufferMemMap = fUserBufferMem->map() ; // if ( !fUserBufferMemMap ) // result = kIOReturnVMError ; // } if ( result == kIOReturnSuccess && !fUserDCLProgramMemPrepared ) { result = fUserDCLProgramMem->prepare() ; fUserDCLProgramMemPrepared = ( result == kIOReturnSuccess ) ; } if (kIOReturnSuccess == result) result = IOFWUserIsochPortProxy::allocatePort(speed, chan) ; else { IOFireWireUserClientLog_(("%s %u IOFWUserLocalIsochPortProxy::allocatePort: failed with error %x\n", __FILE__, __LINE__, result )) ; fPort->release() ; fPort = NULL ; } return result ; } IOReturn IOFWUserLocalIsochPortProxy::releasePort() { // IOFireWireUserClientLog_(("IOFWUserLocalIsochPortProxy::releasePort: zeroing DCL compiler data\n")) ; IOReturn result = kIOReturnSuccess ; if (fPort) { result = IOFWUserIsochPortProxy::releasePort() ; // if ( fUserBufferMemMap ) // { // fUserBufferMemMap->release() ; // fUserBufferMemMap = NULL ; // } if ( fUserBufferMemPrepared ) { fUserBufferMem->complete() ; fUserBufferMemPrepared = false ; } if ( fUserDCLProgramMemPrepared ) { fUserDCLProgramMem->complete() ; fUserDCLProgramMemPrepared = false ; } fPort->release() ; fPort = NULL ; } return result ; } // ============================================================ // utility functions // ============================================================ Boolean IOFWUserLocalIsochPortProxy::getDCLDataBuffer( const DCLCommandStruct* inDCL, IOVirtualAddress* outDataBuffer, IOByteCount* outDataLength) { Boolean result = false ; switch(inDCL->opcode & ~kFWDCLOpFlagMask) { case kDCLSendPacketStartOp: case kDCLSendPacketWithHeaderStartOp: case kDCLSendPacketOp: case kDCLReceivePacketStartOp: case kDCLReceivePacketOp: *outDataBuffer = (IOVirtualAddress)((DCLTransferPacketStruct*)inDCL)->buffer ; *outDataLength = ((DCLTransferPacketStruct*)inDCL)->size ; result = true ; break ; case kDCLSendBufferOp: case kDCLReceiveBufferOp: //zzz what should I do here? break ; default: break ; } return result ; } void IOFWUserLocalIsochPortProxy::setDCLDataBuffer( DCLCommandStruct* inDCL, IOVirtualAddress inDataBuffer, IOByteCount inDataLength) { switch(inDCL->opcode & ~kFWDCLOpFlagMask) { case kDCLSendPacketStartOp: case kDCLSendPacketWithHeaderStartOp: case kDCLSendPacketOp: case kDCLReceivePacketStartOp: case kDCLReceivePacketOp: ((DCLTransferPacketStruct*)inDCL)->buffer = (void*) inDataBuffer ; ((DCLTransferPacketStruct*)inDCL)->size = inDataLength ; break ; case kDCLSendBufferOp: case kDCLReceiveBufferOp: //zzz what should I do here? break ; default: break ; } } IOByteCount IOFWUserLocalIsochPortProxy::getDCLSize( DCLCommandStruct* inDCL) { IOByteCount result = 0 ; switch(inDCL->opcode & ~kFWDCLOpFlagMask) { case kDCLSendPacketStartOp: case kDCLSendPacketWithHeaderStartOp: case kDCLSendPacketOp: case kDCLReceivePacketStartOp: case kDCLReceivePacketOp: result = sizeof(DCLTransferPacketStruct) ; break ; case kDCLSendBufferOp: case kDCLReceiveBufferOp: result = sizeof(DCLTransferBufferStruct) ; break ; case kDCLCallProcOp: result = sizeof(DCLCallProcStruct) ; break ; case kDCLLabelOp: result = sizeof(DCLLabelStruct) ; break ; case kDCLJumpOp: result = sizeof(DCLJumpStruct) ; break ; case kDCLSetTagSyncBitsOp: result = sizeof(DCLSetTagSyncBitsStruct) ; break ; case kDCLUpdateDCLListOp: result = sizeof(DCLUpdateDCLListStruct) ; break ; case kDCLPtrTimeStampOp: result = sizeof(DCLPtrTimeStampStruct) ; } return result ; } void IOFWUserLocalIsochPortProxy::printDCLProgram( const DCLCommandStruct* inDCL, UInt32 inDCLCount) { const DCLCommandStruct* currentDCL = inDCL ; UInt32 index = 0 ; IOLog("IOFWUserLocalIsochPortProxy::printDCLProgram: inDCL=0x%08lX, inDCLCount=%08lX\n", (UInt32)inDCL, (UInt32)inDCLCount) ; while ( (index < inDCLCount) && currentDCL ) { IOLog("\n#0x%04lX @0x%08lX next=0x%08lX, cmplrData=0x%08lX, op=%u ", index, (UInt32)currentDCL, (UInt32)currentDCL->pNextDCLCommand, (UInt32)currentDCL->compilerData, (int) currentDCL->opcode) ; switch(currentDCL->opcode & ~kFWDCLOpFlagMask) { case kDCLSendPacketStartOp: case kDCLSendPacketWithHeaderStartOp: case kDCLSendPacketOp: case kDCLReceivePacketStartOp: case kDCLReceivePacketOp: IOLog("(DCLTransferPacketStruct) buffer=%08lX, size=%u", (UInt32)((DCLTransferPacketStruct*)currentDCL)->buffer, (int)((DCLTransferPacketStruct*)currentDCL)->size) ; break ; case kDCLSendBufferOp: case kDCLReceiveBufferOp: IOLog("(DCLTransferBufferStruct) buffer=%08lX, size=%lu, packetSize=%08X, bufferOffset=%08X", (UInt32)((DCLTransferBufferStruct*)currentDCL)->buffer, ((DCLTransferBufferStruct*)currentDCL)->size, ((DCLTransferBufferStruct*)currentDCL)->packetSize, (UInt32)((DCLTransferBufferStruct*)currentDCL)->bufferOffset) ; break ; case kDCLCallProcOp: IOLog("(DCLCallProcStruct) proc=%08lX, procData=%08lX (OSAsyncRef: ref @ 0x%08lX, userRefCon=%08lX)", (UInt32)((DCLCallProcStruct*)currentDCL)->proc, (UInt32)((DCLCallProcStruct*)currentDCL)->procData, (UInt32)((AsyncRefHolder*)((DCLCallProcStruct*)currentDCL)->procData)->asyncRef, (UInt32)((AsyncRefHolder*)((DCLCallProcStruct*)currentDCL)->procData)->userRefCon) ; break ; case kDCLLabelOp: IOLog("(DCLLabelStruct)") ; break ; case kDCLJumpOp: IOLog("(DCLJumpStruct) pJumpDCLLabel=%08lX", (UInt32)((DCLJumpStruct*)currentDCL)->pJumpDCLLabel) ; break ; case kDCLSetTagSyncBitsOp: IOLog("(DCLSetTagSyncBitsStruct) tagBits=%04lX, syncBits=%04lX", (UInt32)((DCLSetTagSyncBitsStruct*)currentDCL)->tagBits, (UInt32)((DCLSetTagSyncBitsStruct*)currentDCL)->syncBits) ; break ; case kDCLUpdateDCLListOp: IOLog("(DCLUpdateDCLListStruct) dclCommandList=%08lX, numDCLCommands=%lud\n", (UInt32)((DCLUpdateDCLListStruct*)currentDCL)->dclCommandList, ((DCLUpdateDCLListStruct*)currentDCL)->numDCLCommands) ; for(UInt32 listIndex=0; listIndex < ((DCLUpdateDCLListStruct*)currentDCL)->numDCLCommands; listIndex++) { IOLog("%08lX ", (UInt32)(((DCLUpdateDCLListStruct*)currentDCL)->dclCommandList)[listIndex]) ; IOSleep(8) ; } IOLog("\n") ; break ; case kDCLPtrTimeStampOp: IOLog("(DCLPtrTimeStampStruct) timeStampPtr=0x%08lX", (UInt32)((DCLPtrTimeStampStruct*)currentDCL)->timeStampPtr) ; } currentDCL = currentDCL->pNextDCLCommand ; index++ ; IOSleep(40) ; } IOLog("\n") ; if (index != inDCLCount) IOLog("unexpected end of program\n") ; if (currentDCL != NULL) IOLog("program too long for count\n") ; } IOReturn IOFWUserLocalIsochPortProxy::convertToKernelDCL( DCLJumpStruct* inDCLCommand, DCLCommandStruct* inUserDCLTable[], DCLCommandStruct* inUserToKernelDCLLookupTable[], UInt32 inTableLength, UInt32& inOutHint ) { IOReturn result = kIOReturnSuccess ; if ( !userToKernLookup( (DCLCommandStruct*) inDCLCommand->pJumpDCLLabel, inUserDCLTable, inUserToKernelDCLLookupTable, inTableLength, inOutHint, & (DCLCommandStruct*)inDCLCommand->pJumpDCLLabel)) result = kIOReturnError ; IOFireWireUserClientLogIfErr_( result, ("couldn't convert jump DCL (inDCLCommand=0x%08lX, inDCLCommand->pJumpDCLLabel=0x%08lX)\n", (UInt32)inDCLCommand, (UInt32)inDCLCommand->pJumpDCLLabel)) ; return result ; } IOReturn IOFWUserLocalIsochPortProxy::convertToKernelDCL( DCLPtrTimeStampStruct* inDCLCommand, IOVirtualRange* inBufferRanges, UInt32 inBufferRangeCount, IOMemoryDescriptor* inKernelBufferMem) { IOByteCount offset ; IOByteCount tempLength ; IOReturn result = kIOReturnSuccess ; if (!findOffsetInRanges((IOVirtualAddress)(inDCLCommand->timeStampPtr), inBufferRanges, inBufferRangeCount, & offset)) result = kIOReturnError ; // this should never happen! inDCLCommand->timeStampPtr = (UInt32*) inKernelBufferMem->getVirtualSegment(offset, & tempLength) ; if (!inDCLCommand->timeStampPtr) result = kIOReturnVMError ; if (kIOReturnSuccess != result) IOFireWireUserClientLog_(("couldn't convert ptr time stamp DCL\n")) ; return result ; } IOReturn IOFWUserLocalIsochPortProxy::convertToKernelDCL( DCLUpdateDCLListStruct* inDCLCommand, DCLCommandStruct* inUserDCLTable[], DCLCommandStruct* inUserToKernelDCLLookupTable[], UInt32 inTableLength, UInt32& inOutHint ) { IOReturn result = kIOReturnSuccess ; IOByteCount listSize = inDCLCommand->numDCLCommands * sizeof(DCLCommandStruct*) ; IOMemoryDescriptor* dclListMem = IOMemoryDescriptor::withAddress( (IOVirtualAddress) inDCLCommand->dclCommandList, listSize, kIODirectionIn, fUserClient->getOwningTask() ) ; if (!dclListMem) result = kIOReturnNoMemory ; if ( kIOReturnSuccess == result ) { inDCLCommand->dclCommandList = new (DCLCommandStruct*)[inDCLCommand->numDCLCommands] ; if (!inDCLCommand->dclCommandList) result = kIOReturnNoMemory ; } if ( kIOReturnSuccess == result ) { if ( dclListMem->readBytes(0, inDCLCommand->dclCommandList, listSize) < listSize ) result = kIOReturnVMError ; } if ( kIOReturnSuccess == result ) { for(UInt32 index = 0; index < inDCLCommand->numDCLCommands; index++) { if ( !userToKernLookup( inDCLCommand->dclCommandList[index], inUserDCLTable, inUserToKernelDCLLookupTable, inTableLength, inOutHint, & (inDCLCommand->dclCommandList)[index])) result = kIOReturnError ; } } if (dclListMem) { dclListMem->release() ; } if (kIOReturnSuccess != result) IOFireWireUserClientLog_(("couldn't convert update dcl list\n")) ; return result ; } IOReturn IOFWUserLocalIsochPortProxy::convertToKernelDCL( DCLCallProcStruct* inDCLCommand, DCLCommandStruct* inUserDCL ) { IOReturn result = kIOReturnSuccess ; AsyncRefHolder* holder = new AsyncRefHolder ; if (!holder) result = kIOReturnNoMemory ; else { (holder->asyncRef)[0] = 0 ; holder->userRefCon = inUserDCL ; holder->obj = this ; inDCLCommand->procData = (UInt32) holder ; inDCLCommand->proc = & (IOFWUserLocalIsochPortProxy::dclCallProcHandler) ; } if (kIOReturnSuccess != result) IOFireWireUserClientLog_(("couldn't convert call proc DCL\n")) ; return result ; } Boolean IOFWUserLocalIsochPortProxy::findOffsetInRanges( IOVirtualAddress inAddress, IOVirtualRange inRanges[], UInt32 inRangeCount, IOByteCount* outOffset) { UInt32 index = 0 ; IOByteCount distanceInRange ; Boolean found = false ; *outOffset = 0 ; while (!found && (index < inRangeCount)) { distanceInRange = inAddress - inRanges[index].address ; if (found = ((distanceInRange >= 0) && (distanceInRange < inRanges[index].length))) *outOffset += distanceInRange ; else *outOffset += inRanges[index].length ; index++ ; } return found ; } Boolean IOFWUserLocalIsochPortProxy::userToKernLookup( DCLCommandStruct* inDCLCommand, DCLCommandStruct* inUserDCLList[], DCLCommandStruct* inKernDCLList[], UInt32 inTableLength, UInt32& inOutHint, DCLCommandStruct** outDCLCommand) { inOutHint = (inOutHint - 1) % inTableLength ; UInt32 tableIndex = inOutHint ; do { if ( inUserDCLList[tableIndex] != inDCLCommand ) { tableIndex = (tableIndex + 1) % inTableLength ; } else { *outDCLCommand = inKernDCLList[tableIndex] ; inOutHint = tableIndex ; if ( inDCLCommand == NULL ) IOFireWireUserClientLog_(("IOFWUserLocalIsochPortProxy::userToKernLookup: ")) ; return true ; } } while ( tableIndex != inOutHint ) ; IOFireWireUserClientLog_(("IOFWUserLocalIsochPortProxy::userToKernLookup: couldn't find 0x%08lX\n", (UInt32)inDCLCommand)) ; return false ; } void IOFWUserLocalIsochPortProxy::dclCallProcHandler( DCLCommandStruct* pDCLCommand) { AsyncRefHolder* holder = (AsyncRefHolder*) (((DCLCallProcStruct*)pDCLCommand)->procData) ; #ifdef IOFIREWIREUSERCLIENTDEBUG IOFWUserLocalIsochPortProxy* me = (IOFWUserLocalIsochPortProxy*) holder->obj ; me->fUserClient->getStatistics()->isochCallbacks->addValue( 1 ) ; #endif // if (count++ % 100 == 0) // IOFireWireUserClientLog_(("callback called 100 times\n")) ; if ((holder->asyncRef)[0]) { // IOFireWireUserClientLog_(("holder->asyncRef[0]=0x%08lX, callback=%08lX\n", holder->asyncRef[0], (holder->asyncRef)[kIOAsyncCalloutFuncIndex])) ; IOReturn result = IOFireWireUserClient::sendAsyncResult( holder->asyncRef, kIOReturnSuccess, NULL, 0) ; if (kIOReturnSuccess != result) IOFireWireUserClientLog_(("IOFWUserLocalIsochPortProxy::dclCallProcHandler: sendAsyncResult returned error 0x%08lX!\n", (UInt32) result)) ; } } IOReturn IOFWUserLocalIsochPortProxy::setAsyncRef_DCLCallProc( OSAsyncReference asyncRef, DCLCallCommandProcPtr inProc) { DCLCommandStruct* pCurrentDCL = fKernDCLProgramStart ; AsyncRefHolder* holder ; // we walk the DCL program looking for callproc DCLs. // when we find them, we update their asyncRef's as appropriate while (pCurrentDCL) // should be careful, this has potential to loop forever if passed an endless program { if ((pCurrentDCL->opcode & ~kFWDCLOpFlagMask) == kDCLCallProcOp) { holder = (AsyncRefHolder*)((DCLCallProcStruct*)pCurrentDCL)->procData ; IOFireWireUserClient::setAsyncReference( asyncRef, (mach_port_t) asyncRef[0], (void*) inProc, holder->userRefCon ); bcopy( asyncRef, & holder->asyncRef, sizeof(OSAsyncReference)) ; } pCurrentDCL = pCurrentDCL->pNextDCLCommand ; } return kIOReturnSuccess ; } IOReturn IOFWUserLocalIsochPortProxy::modifyJumpDCL( UInt32 inJumpDCLCompilerData, UInt32 inLabelDCLCompilerData) { inJumpDCLCompilerData-- ; inLabelDCLCompilerData-- ; // be sure opcodes exist if ( inJumpDCLCompilerData > fUserToKernelDCLLookupTableLength || inLabelDCLCompilerData > fUserToKernelDCLLookupTableLength ) { IOFireWireUserClientLog_(("IOFWUserLocalIsochPort::modifyJumpDCL: DCL index (inJumpDCLCompilerData=%u, inLabelDCLCompilerData=%u) past end of lookup table (length=%u)\n", inJumpDCLCompilerData, inLabelDCLCompilerData, fUserToKernelDCLLookupTableLength)) ; return kIOReturnBadArgument ; } DCLJumpStruct* pJumpDCL = (DCLJumpStruct*)(fUserToKernelDCLLookupTable[inJumpDCLCompilerData]) ; pJumpDCL->pJumpDCLLabel = (DCLLabelStruct*)(fUserToKernelDCLLookupTable[inLabelDCLCompilerData]) ; // // make sure we're modifying a jump and that it's pointing to a label if ((pJumpDCL->opcode & ~kFWDCLOpFlagMask) != kDCLJumpOp || (pJumpDCL->pJumpDCLLabel->opcode & ~kFWDCLOpFlagMask) != kDCLLabelOp) { IOFireWireUserClientLog_(("IOFWUserLocalIsochPortProxy::modifyJumpDCL: modifying non-jump or pointing jump to non-label\n")) ; return kIOReturnBadArgument ; } if (!pJumpDCL->compilerData) return kIOReturnSuccess ; if (fPort) return ((IOFWLocalIsochPort*)fPort)->notify(kFWDCLModifyNotification, & (DCLCommandStruct*)pJumpDCL, 1) ; else return kIOReturnSuccess ; } IOReturn IOFWUserLocalIsochPortProxy::createPort() { IOReturn result = kIOReturnSuccess ; // if ( !fUserDCLProgramMemPrepared ) // { // result = fUserDCLProgramMem->prepare(kIODirectionOutIn) ; // fUserDCLProgramMemPrepared = (kIOReturnSuccess == result) ; // // IOFireWireUserClientLogIfFalse_( fUserDCLProgramMemPrepared, ("%s %u: prepare result=%08lX", __FILE__, __LINE__, result ) ) ; // } if ( !fUserBufferMemPrepared ) { result = fUserBufferMem->prepare(kIODirectionOutIn) ; fUserBufferMemPrepared = (kIOReturnSuccess == result) ; } if ( result == kIOReturnSuccess ) { fPort = fUserClient->getOwner()->getController()->createLocalIsochPort( fTalking, fKernDCLProgramStart, 0, fStartEvent, fStartState, fStartMask) ; if (!fPort) return kIOReturnNoMemory ; } return kIOReturnSuccess ; }