/* * 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. * */ #import "IOFireWireDevice.h" #import "IOFireWireController.h" #import "IOFireWireLink.h" #import "IOFireWireUserClient.h" #import "IOFWUserIsochPort.h" #import "IOFWLocalIsochPort.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 ; fPortStarted = false ; fPortAllocated = false ; fPort = NULL ; IOFireWireUserClientLog_("IOFWUserIsochPortProxy::init: this=%p, fPort %p, fUserClient %p\n", this, fPort, fUserClient) ; 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 %p\n", this) ; fPortStarted = (kIOReturnSuccess == result) ; } return result ; } IOReturn IOFWUserIsochPortProxy::stop() { IOReturn result = kIOReturnSuccess ; if (fPortStarted) { IOFireWireUserClientLog_("IOFWUserIsochPortProxy::stop: stopping port %p\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=%p, releasing fPort @ %p\n", this, fPort) ; if (fPort) { fPort->release() ; fPort = NULL ; } OSObject::free() ; } IOReturn IOFWUserIsochPortProxy::createPort() { IOReturn result = kIOReturnSuccess ; IOFWUserIsochPort* newPort = new IOFWUserIsochPort ; if (newPort && !newPort->init()) { newPort->release() ; newPort = NULL ; result = kIOReturnNoMemory ; IOLog("%s %u: couldn't make newPort!\n", __FILE__, __LINE__) ; } fPort = newPort ; return result ; } #pragma mark - #pragma mark --- IOFWUserLocalIsochPortProxy ---------- /****************************************************************/ OSDefineMetaClassAndStructors(IOFWUserLocalIsochPortProxy, IOFWUserIsochPortProxy) /****************************************************************/ Boolean IOFWUserLocalIsochPortProxy::initWithUserDCLProgram( LocalIsochPortAllocateParams* inParams, IOFireWireUserClient* inUserClient) { if (!IOFWUserIsochPortProxy::init(inUserClient)) { IOLog("%s %u:IOFWUserIsochPortProxy::init failed\n", __FILE__, __LINE__) ; return false ; } fTalking = inParams->talking ; fStartEvent = inParams->startEvent ; fStartState = inParams->startState ; fStartMask = inParams->startMask ; fUserObj = inParams->userObj ; fTaskInfo.fTask = fUserClient->getOwningTask(); // // 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 ; // // 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) { IOLog("%s %u: Couldn't create programRangesMem\n", __FILE__, __LINE__) ; 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) { IOLog("%s %u: Couldn't get fUserDCLProgramMem\n", __FILE__, __LINE__) ; 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 ; IOLog("%s %u: couldn't get fKernDCLProgramBuffer\n", __FILE__, __LINE__) ; } else fKernDCLProgramStart = (DCLCommand*)(fKernDCLProgramBuffer + programStartOffset) ; } // // === Map user data buffers into kernel // // create an array to hold list of user space ranges if ( inParams->userDCLBufferRangeCount > 0 ) { if ( kIOReturnSuccess == result ) { if ( NULL == ( bufferRanges = new IOVirtualRange[ inParams->userDCLBufferRangeCount ] ) ) { result = kIOReturnNoMemory ; IOLog("%s %u: couldn't get bufferRanges\n", __FILE__, __LINE__) ; } } // 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), kIODirectionOutIn, fUserClient->getOwningTask() ) ; if (!bufferRangesMem) { IOLog("%s %u: couldn't get bufferRangesMem\n", __FILE__, __LINE__) ; 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() ; } // 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 ) { IOLog("%s %u: couldn't get fUserBufferMem\n", __FILE__, __LINE__) ; result = kIOReturnNoMemory ; } } if ( kIOReturnSuccess == result ) { result = fUserBufferMem->prepare( kIODirectionOutIn ) ; fUserBufferMemPrepared = ( kIOReturnSuccess == result ) ; if (!fUserBufferMemPrepared) IOLog("%s %u:couldn't prepare mem\n", __FILE__, __LINE__) ; } } // allocate a buffer to hold the user space --> kernel dcl mapping table if (kIOReturnSuccess == result) { fUserToKernelDCLLookupTableLength = inParams->userDCLProgramDCLCount ; fUserToKernelDCLLookupTable = new DCLCommand*[ fUserToKernelDCLLookupTableLength ] ; if ( !fUserToKernelDCLLookupTable ) { IOLog("%s %u: couldn't get fUserToKernelDCLLookupTable\n", __FILE__, __LINE__) ; result = kIOReturnNoMemory ; } } DCLCommand** userDCLTable = NULL ; // array if (kIOReturnSuccess == result) { userDCLTable = new (DCLCommand*)[ fUserToKernelDCLLookupTableLength ] ; if (!userDCLTable) { IOLog("%s %u: couldn't get userDCLTable\n", __FILE__, __LINE__) ; 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 if ( fUserBufferMem ) { fUserBufferMemMap = fUserBufferMem->map() ; if ( !fUserBufferMemMap ) { fKernDCLProgramStart = NULL ; result = kIOReturnVMError ; IOLog("%s %u: couldn't map!\n", __FILE__, __LINE__) ; } else { fTaskInfo.fDataBaseAddr = fUserBufferMemMap->getVirtualAddress(); fTaskInfo.fDataSize = fUserBufferMemMap->getLength(); // Horrible hack!! fTaskInfo.fCallRefCon = fUserBufferMem; } } if (fKernDCLProgramStart) { DCLCommand* pCurrentKernDCL = fKernDCLProgramStart ; UInt32 lookupTableLength = 0 ; DCLCommand* 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 ( tempBufferRange.address != NULL && tempBufferRange.length > 0 ) { 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 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") ; pCurrentKernDCL->pNextDCLCommand = NULL ; } else pCurrentKernDCL->pNextDCLCommand = (DCLCommand*)(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() ; IOFireWireUserClientLog_("releasing fUserBufferMem, retain=%u\n", fUserBufferMem->getRetainCount()) ; fUserBufferMem->release() ; } if ( fUserDCLProgramMem ) { if ( fUserDCLProgramMemPrepared ) fUserDCLProgramMem->complete() ; fUserDCLProgramMem->release() ; fUserDCLProgramMem = NULL ; } delete[] fUserToKernelDCLLookupTable ; if (fKernDCLProgramBuffer) { DCLCommand* 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 && fUserBufferMem && !fUserBufferMemPrepared ) { result = fUserBufferMem->prepare() ; fUserBufferMemPrepared = ( result == kIOReturnSuccess ) ; } 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() { IOReturn result = kIOReturnSuccess ; if (fPort) { result = IOFWUserIsochPortProxy::releasePort() ; if ( fUserBufferMemPrepared ) { fUserBufferMem->complete() ; fUserBufferMemPrepared = false ; } if ( fUserDCLProgramMemPrepared ) { fUserDCLProgramMem->complete() ; fUserDCLProgramMemPrepared = false ; } fPort->release() ; fPort = NULL ; } return result ; } IOReturn IOFWUserLocalIsochPortProxy::stop() { IOFireWireLink* link = fUserClient->getOwner()->getController()->getLink() ; // we are sending a stop token, but take isoch workloop lock to make sure all // callbacks coming from FWIM have already been cleared out. // workLoop->(IOWorkLoop::Action*) // workLoop->closeGate() ; link->closeIsochGate() ; #if IOFIREWIREDEBUG > 0 IOReturn result = #endif IOFireWireUserClient::sendAsyncResult( fStopTokenAsyncRef, kIOFireWireLastDCLToken, & fUserObj, 1 ) ; IOFireWireUserClientLogIfErr_(result, "IOFWUserLocalIsochPortProxy::stop: sendAsyncResult returned error 0x%08lX!\n", (UInt32) result) ; IOReturn err = IOFWUserIsochPortProxy::stop() ; link->openIsochGate() ; return err ; } // ============================================================ // utility functions // ============================================================ Boolean IOFWUserLocalIsochPortProxy::getDCLDataBuffer( const DCLCommand* 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 ; case kDCLPtrTimeStampOp: *outDataBuffer = (IOVirtualAddress)((DCLPtrTimeStamp*)inDCL)->timeStampPtr ; *outDataLength = sizeof( *(((DCLPtrTimeStamp*)inDCL)->timeStampPtr) ) ; result = true ; break ; default: break ; } return result ; } void IOFWUserLocalIsochPortProxy::setDCLDataBuffer( DCLCommand* 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 ; case kDCLPtrTimeStampOp: ((DCLPtrTimeStamp*)inDCL)->timeStampPtr = (UInt32*)inDataBuffer ; break ; default: break ; } } IOByteCount IOFWUserLocalIsochPortProxy::getDCLSize( DCLCommand* 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 DCLCommand* inDCL, UInt32 inDCLCount) { const DCLCommand* 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=%08lX", (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, DCLCommand* inUserDCLTable[], DCLCommand* inUserToKernelDCLLookupTable[], UInt32 inTableLength, UInt32& inOutHint ) { IOReturn result = kIOReturnSuccess ; if ( !userToKernLookup( (DCLCommand*) inDCLCommand->pJumpDCLLabel, inUserDCLTable, inUserToKernelDCLLookupTable, inTableLength, inOutHint, & (DCLCommand*)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( DCLUpdateDCLListStruct* inDCLCommand, DCLCommand* inUserDCLTable[], DCLCommand* inUserToKernelDCLLookupTable[], UInt32 inTableLength, UInt32& inOutHint ) { IOReturn result = kIOReturnSuccess ; IOByteCount listSize = inDCLCommand->numDCLCommands * sizeof(DCLCommand*) ; IOMemoryDescriptor* dclListMem = IOMemoryDescriptor::withAddress( (IOVirtualAddress) inDCLCommand->dclCommandList, listSize, kIODirectionIn, fUserClient->getOwningTask() ) ; if (!dclListMem) { IOLog("%s %u: couldn't get dclListMem\n", __FILE__, __LINE__) ; result = kIOReturnNoMemory ; } if ( kIOReturnSuccess == result ) { inDCLCommand->dclCommandList = new (DCLCommand*)[inDCLCommand->numDCLCommands] ; if (!inDCLCommand->dclCommandList) { IOFireWireUserClientLog_("%s %u: couldn't allocate inDCLCommand->dclCommandList\n", __FILE__, __LINE__) ; result = kIOReturnNoMemory ; } } if ( kIOReturnSuccess == result ) { if ( dclListMem->readBytes(0, inDCLCommand->dclCommandList, listSize) < listSize ) { IOFireWireUserClientLog_("%s %u: dclListMem->readBytes failed\n", __FILE__, __LINE__) ; 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])) { IOFireWireUserClientLog_("%s %u: couldn't find dcl referenced in dcl list in program\n", __FILE__, __LINE__) ; result = kIOReturnError ; } } } if (dclListMem) { dclListMem->release() ; } return result ; } IOReturn IOFWUserLocalIsochPortProxy::convertToKernelDCL( DCLCallProcStruct* inDCLCommand, DCLCommand* inUserDCL ) { IOReturn result = kIOReturnSuccess ; AsyncRefHolder* holder = new AsyncRefHolder ; if (!holder) { IOLog("%s %u: couldn't get holder\n", __FILE__, __LINE__) ; result = kIOReturnNoMemory ; } else { (holder->asyncRef)[0] = 0 ; holder->userRefCon = inUserDCL ; holder->obj = this ; inDCLCommand->procData = (UInt32) holder ; inDCLCommand->proc = & (IOFWUserLocalIsochPortProxy::dclCallProcHandler) ; } IOFireWireUserClientLogIfErr_(result, "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( DCLCommand* inDCLCommand, DCLCommand* inUserDCLList[], DCLCommand* inKernDCLList[], UInt32 inTableLength, UInt32& inOutHint, DCLCommand** 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( DCLCommand* pDCLCommand) { AsyncRefHolder* holder = (AsyncRefHolder*) (((DCLCallProcStruct*)pDCLCommand)->procData) ; IOFWUserLocalIsochPortProxy* me = (IOFWUserLocalIsochPortProxy*) holder->obj ; #if IOFIREWIREUSERCLIENTDEBUG > 0 if (!me->fUserClient->getStatistics()) panic("%s %u", __FILE__, __LINE__) ; me->fUserClient->getStatistics()->isochCallbacks->addValue( 1 ) ; #endif if ((holder->asyncRef)[0]) { #if IOFIREWIREUSERCLIENTDEBUG > 0 IOReturn result = #endif IOFireWireUserClient::sendAsyncResult( holder->asyncRef, kIOReturnSuccess, & me->fUserObj, 1) ; IOFireWireUserClientLogIfErr_(result, "IOFWUserLocalIsochPortProxy::dclCallProcHandler: sendAsyncResult returned error 0x%08lX!\n", (UInt32) result) ; } } IOReturn IOFWUserLocalIsochPortProxy::setAsyncRef_DCLCallProc( OSAsyncReference asyncRef, DCLCallCommandProc* inProc ) { DCLCommand* pCurrentDCL = fKernDCLProgramStart ; AsyncRefHolder* holder ; // set up stop token async ref IOFireWireUserClient::setAsyncReference( asyncRef, (mach_port_t) asyncRef[0], (void*)inProc, (void*)0xFFFFFFFF ) ; bcopy( asyncRef, fStopTokenAsyncRef, sizeof( OSAsyncReference ) ) ; // 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=%lu, inLabelDCLCompilerData=%lu) past end of lookup table (length=%lu)\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->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, & (DCLCommand*)pJumpDCL, 1) ; return kIOReturnSuccess ; } IOReturn IOFWUserLocalIsochPortProxy::modifyJumpDCLSize( UInt32 dclCompilerData, IOByteCount newSize ) { --dclCompilerData ; // be sure opcodes exist if ( dclCompilerData > fUserToKernelDCLLookupTableLength ) { IOFireWireUserClientLog_("IOFWUserLocalIsochPort::modifyJumpDCLSize: DCL index (dclCompilerData=%lu) past end of lookup table (length=%lu)\n", dclCompilerData, fUserToKernelDCLLookupTableLength ) ; return kIOReturnBadArgument ; } DCLTransferPacket* dcl = (DCLTransferPacket*)( fUserToKernelDCLLookupTable[ dclCompilerData ] ) ; if (fPort) return ((IOFWLocalIsochPort*)fPort)->notify(kFWDCLModifyNotification, &(DCLCommand*)dcl, 1) ; return kIOReturnSuccess ; } IOReturn IOFWUserLocalIsochPortProxy::createPort() { IOReturn result = kIOReturnSuccess ; if ( fUserBufferMem && !fUserBufferMemPrepared ) { result = fUserBufferMem->prepare(kIODirectionOutIn) ; fUserBufferMemPrepared = (kIOReturnSuccess == result) ; } if ( result == kIOReturnSuccess ) { fPort = fUserClient->getOwner()->getController()->createLocalIsochPort( fTalking, fKernDCLProgramStart, &fTaskInfo, fStartEvent, fStartState, fStartMask) ; if (!fPort) { IOLog("%s %u: no fPort", __FILE__, __LINE__) ; return kIOReturnNoMemory ; } } return kIOReturnSuccess ; }