/* * Copyright (c) 1998-2002 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The 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, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include "DVLib.h" #include #include #include #include #include // Debug messages #include #include #include #include #include "IsochronousDataHandler.h" #define kDVRequestID 0 #define ALT_TIMING 1 //#define USE_P2P_CONNECTIONS_FOR_DV_READ 1 #define PAGE_SIZE 4096; #define kNoPlug 0xdeadbeef // Use Channel 62 to send DV, and 63 to receive (because camcorders love to send on 63). #define kWriteChannel 62 #define kReadChannel 61 enum { kNoP2PConnection = 0, kWriteP2PConnection = 1, kReadP2PConnection = 2 }; // AY_TODO: Move the following to IOFireWireAVCConsts.h #define kAVCSignalModeDVCPro50_525_60 0x74 #define kAVCSignalModeDVCPro50_625_50 0xF4 #define kAVCSignalModeDVCPro100_525_60 0x70 #define kAVCSignalModeDVCPro100_625_50 0xF0 #define kAVCSignalModeMask_DVCPro50 0x74 #define kAVCSignalModeMask_DVCPro100 0x70 #define kAVCSignalModeMask_HD 0x08 enum { kDVRunning = 0, kDVStopped = 1, kDVWriteOverrun = 2, kDVReadOverrun = 3, kDVStopping = 4 }; // Isoc packet header, 2nd quad enum { kPALBit = 0x800000, // 1 bit kSTypeMask = 0x7c0000, // 5 bits kSTypeSD = 0x00000, kSTypeSDL = 0x40000, kSTypeHD = 0x80000 }; enum { // DVRead kNumPacketsPerInputBuffer = 100, kDVSDPayloadPacketSize = 480, kDVSDLPayloadPacketSize = 240, kDVHDPayloadPacketSize = 960, kDVCPro50PayloadPacketSize = 960, kDVCPro100PayloadPacketSize = 1920, kNumPingPongs = 8, // Number of DCL blocks in read program kNumPacketsPerPingPong = 100, kNumDCLsPerPingPongPacket = 1, kRecordNumDCLs = kNumPingPongs * (kNumPacketsPerPingPong * kNumDCLsPerPingPongPacket+3)+6, kMaxDCLSize = 32, kRecordDCLProgramSize = kMaxDCLSize * kRecordNumDCLs, }; enum { kNTSCFrameRateNumerator = 2997, kNTSCFrameRateDenominator = 100, kNTSCPlayFramePeriodNumerator = kNTSCFrameRateDenominator, kNTSCPlayFramePeriodDenominator = kNTSCFrameRateNumerator, kNTSCNumDataPacketsPerDVFrame = 250, kNTSCNumDataPacketsPerGroup = 125, kPALFrameRateNumerator = 25, kPALFrameRateDenominator = 1, kPALPlayFramePeriodNumerator = kPALFrameRateDenominator, kPALPlayFramePeriodDenominator = kPALFrameRateNumerator, kPALNumDataPacketsPerDVFrame = 300, kPALNumDataPacketsPerGroup = 150, kDVPacketAlignSlop = 8, // add 8 bytes to kDVPacketCIPSize = 8, kPlaySYTDelay = 3, // Sony camcorders send a delay of 2, but VX2000 wants 3 from us... }; // Frame Sizes enum { kFrameSize_SD525_60 = 120000, kFrameSize_DVCPro525_60 = 120000, kFrameSize_SD625_50 = 144000, kFrameSize_DVCPro625_50 = 144000, kFrameSize_SDL525_60 = 60000, kFrameSize_SDL625_50 = 72000, kFrameSize_DVCPro50_525_60 = 240000, kFrameSize_HD1125_60 = 240000, kFrameSize_DVCPro50_625_50 = 288000, kFrameSize_HD1250_50 = 288000, kFrameSize_DVCPro100_525_60 = 480000, kFrameSize_DVCPro100_625_50 = 576000 }; typedef struct { UInt32 fRequest; void * fParams; } DVReq; typedef struct { mach_msg_header_t msgHdr; DVReq dvRequest; } SendMsg; typedef struct { mach_msg_header_t msgHdr; DVReq dvRequest; mach_msg_trailer_t trailer; } ReceiveMsg; struct DVLocalOutStruct; typedef struct DVLocalOutStruct DVLocalOut, *DVLocalOutPtr; typedef struct _DVStreamStruct { IOFireWireLibDeviceRef pFWDevice; IOFireWireAVCLibProtocolInterface **fAVCProtoInterface; IOFireWireLibLocalIsochPortRef pFWLocalIsochPort; IOFireWireLibRemoteIsochPortRef pFWRemoteIsochPort; IOFireWireLibIsochChannelRef fIsochChannelRef; UInt32 fPlug; // PCR plug register of local node DVThread * fThread; DVFrameVars fFrames; DCLCommandPtr pDCLList; // DCLs used for playing. UInt8 * fDCLBuffers; // Buffers to use to transfer packet data. UInt32 fDCLBufferSize; // Total allocation for output buffers UInt64 fChannelMask; // Legal channels to use UInt8 fSignalMode; // signal type UInt8 fIsocChannel; // Channel to use UInt8 fMaxSpeed; // Max bus speed for isoc channel DVDevice *pDVDevice; UInt32 fDVFrameSize; // Frame size based on current signal mode } DVStream; // structs struct DVLocalOutStruct { DVLocalOutPtr pNextLocalData; DVLocalOutPtr pPrevLocalData; DVGlobalOutPtr pGlobalData; // Pointer to jump command to end of buffer group. DCLJumpPtr pEndOfBufferGroupDCLJump; // Pointer to label command at start of buffer group. DCLLabelPtr pStartOfBufferGroupDCLLabel; // Pointer to jump command to use to skip an empty packet. DCLJumpPtr pBufferGroupSkipEmptyPacketDCLJump; // Label to jump to to skip an empty packet. DCLLabelPtr pBufferGroupSkipEmptyPacketDCLLabel; // Label to jump to to not skip an empty packet. DCLLabelPtr pBufferGroupDontSkipEmptyPacketDCLLabel; // Pointer to buffer group's time stamp. UInt32 * pBufferGroupTimeStampPtr; // Pointer to first DCL command in buffer group. DCLCommandPtr pFirstBufferGroupDCLCommand; // Pointer to last DCL command in buffer group. DCLCommandPtr pLastBufferGroupDCLCommand; // DCL update list with just the buffer group's time stamp DCL. DCLCommandPtr timeStampUpdateDCLList; // List of DCL commands that need updating for each run through buffer group. DCLCommandPtr * bufferGroupUpdateDCLList; // Number of DCL commands in above list. UInt32 updateListSize; // Number of packets in this buffer group. UInt32 numPackets; UInt32 fBlockNum; // True if pBufferGroupSkipEmptyPacketDCLJump is set to skip an empty packet. bool skippingEmptyPacket; }; // DVGlobalOut struct DVGlobalOutStruct { DVStream fStreamVars; UInt8 * fDCLCommandPool; UInt32 fTotalPool; UInt32 fAllocatedPool; DVSharedVars fSharedDCLVars; // output structures DVLocalOut fLocalDataArray[kNumPlayBufferGroups]; // List of buffer group data records. UInt8 * pEmptyTransmitBuffers; // Buffer to use for empty transmit packet data. DCLLabelPtr pUnderrunDCLLabel; // Pointer to underrun label. UInt32 totalPackets; // Total number of packets for playing. UInt32 activePackets; // Total number of active packets for playing. UInt32 nominalFrameCycleTime; // Nominal cycle time between frames. UInt32 nextSYT; // Next SYT to use on frame boundary. UInt32 nextDBC; // Next DBC to use for first packet of next buffer group. UInt32 numDataPacketsPerFrame; // number of data packets per frame (different for NTSC or PAL) UInt32 numDataPacketsPerGroup; // Number of data packets in each group (different for NTSC or PAL) UInt32 playFramePeriodNumerator; UInt32 playFramePeriodDenominator; UInt32 playFrameRateNumerator, playFrameRateDenominator; UInt32 fDataPacketSize; // Data portion, in bytes UInt32 fDataQuadSize; // Data portion, in quads UInt32 fAlignQuadSize; // Packet size in quads, padded to 16 byte boundary. UInt32 fHeader0; // First quad - sourceID, data size, seq. no. UInt32 fHeader1; // Second quad - format, PAL/NTSC, SType, rsv, SYT // channel buffer structures UInt32 nextDataPacketNum; // Data packet number for first data packet of next buffer group. UInt32 * pImageBuffer; // Buffer to hold image in. bool fUpdateBuffers; // Our job to copy image data? bool pendingDVWriteUnderrunHandler; bool deferredDVWriteFree; bool dvWriteStopInProgress; }; typedef struct DVLocalInStruct { DVGlobalInPtr pGlobalData; DCLCommandPtr fFirstCmd; DCLLabelPtr fStateLabel; DCLJumpPtr fStateJmp; UInt32 fBlockNum; UInt32 * fTimeStampPtr; } DVLocalIn, *DVLocalInPtr; struct DVGlobalInStruct { DVStream fStreamVars; DCLCommandPtr *ppUpdateDCLList; UInt32 packetCount; UInt32 fLastFrameTime; DVLocalIn fLocalDataArray[kNumPingPongs]; DCLLabelPtr fTerminal; UInt8 *pImageBuffer; UInt8 lastSequenceCount; UInt8 fState; // Current DCL block UInt8 fSynced; UInt8 fRestarted; bool pendingDVReadUnderrunHandler; bool deferredDVReadFree; bool dvReadStopInProgress; }; static IOReturn buildWriteProgram(DVGlobalOutPtr pGlobalData); static IOReturn allocateBuffers(DVGlobalOutPtr pGlobalData); static void DVWritePoll(DVGlobalOutPtr globs); static void DVReadPoll(DVGlobalInPtr globs); static void closeStream(DVStream *stream); static void doDVReadHandleInputUnderrun( DVGlobalInPtr pGlobalData ); static void doDVHandleOutputUnderrun( DVGlobalOutPtr pGlobalData ); UInt32 AddFWCycleTimeToFWCycleTime( UInt32 cycleTime1, UInt32 cycleTime2 ) { UInt32 secondCount, cycleCount, cycleOffset; UInt32 cycleTime; // Add cycle offsets. cycleOffset = (cycleTime1 & 0x0FFF) + (cycleTime2 & 0x0FFF); // Add cycle counts. cycleCount = (cycleTime1 & 0x01FFF000) + (cycleTime2 & 0x01FFF000); // Add any carry over from cycle offset to cycle count. if (cycleOffset > 3071) { cycleCount += 0x1000; cycleOffset -= 3072; } // Add secondCounts. secondCount = (cycleTime1 & 0xFE000000) + (cycleTime2 & 0xFE000000); // Add any carry over from cycle count to secondCount. if (cycleCount > (7999 << 12)) { secondCount += 0x02000000; cycleCount -= (8000 << 12); } // Put everything together into cycle time. cycleTime = secondCount | cycleCount | cycleOffset; return (cycleTime); } UInt32 SubtractFWCycleTimeFromFWCycleTime( UInt32 cycleTime1, UInt32 cycleTime2) { SInt32 secondCount, cycleCount, cycleOffset; UInt32 cycleTime; // Subtract cycle offsets. cycleOffset = (cycleTime1 & 0x0FFF) - (cycleTime2 & 0x0FFF); // Subtract cycle counts. cycleCount = (cycleTime1 & 0x01FFF000) - (cycleTime2 & 0x01FFF000); // Subtract any borrow over from cycle offset to cycle count. if (cycleOffset < 0) { cycleCount -= 0x1000; cycleOffset += 3072; } // Subtract secondCounts. secondCount = (cycleTime1 & 0xFE000000) - (cycleTime2 & 0xFE000000); // Subtract any borrow over from cycle count to secondCount. if (cycleCount < 0) { secondCount -= 0x02000000; cycleCount += (8000 << 12); } // Put everything together into cycle time. cycleTime = secondCount | cycleCount | cycleOffset; return (cycleTime); } static UInt32 ConvertFractionalSecondsToFWCycleTime( UInt32 secondsNumerator, UInt32 secondsDenominator ) { // float fSecondCount; // float fCycleCount; // float fCycleOffset; // UInt32 iSecondsCount; // UInt32 iCycleCount; // UInt32 iCycleOffset; // UInt32 secondsCycleTime; UInt32 iSecondsCount2; UInt32 iCycleCount2; UInt32 iCycleOffset2; UInt32 mSecondCount; UInt32 mCycleCount; UInt32 secondsCycleTime2; // Convert fractional seconds into floating point and compute seconds count. // fSecondCount = ((float) secondsNumerator) / ((float) secondsDenominator); // iSecondsCount = (UInt32) fSecondCount; iSecondsCount2 = secondsNumerator / secondsDenominator; mSecondCount = secondsNumerator % secondsDenominator; // Subtract whole seconds out of fSecondCount and convert to cycle count. // fCycleCount = (fSecondCount - ((float) iSecondsCount)) * 8000.0; // iCycleCount = (UInt32) fCycleCount; iCycleCount2 = (mSecondCount * 8000) / secondsDenominator; mCycleCount = (mSecondCount * 8000) % secondsDenominator; // Subtract whole cycles out of fCycleCount and convert to cycle offset. // fCycleOffset = (fCycleCount - ((float) iCycleCount)) * 3072.0; // iCycleOffset = (UInt32) fCycleOffset; iCycleOffset2 = (mCycleCount * 3072) / secondsDenominator; // Convert to cycle timer format. // secondsCycleTime = (iSecondsCount << 25) | (iCycleCount << 12) | iCycleOffset; secondsCycleTime2 = (iSecondsCount2 << 25) | (iCycleCount2 << 12) | iCycleOffset2; return (secondsCycleTime2); } static IOReturn writePlug(IOFireWireAVCLibProtocolInterface **interface, UInt32 plug, UInt32 val) { return (*interface)->updateOutputPlug(interface, plug, (*interface)->readOutputPlug(interface, plug), val); } static void handlePCRLock(void *refcon, UInt32 generation, UInt16 nodeID, UInt32 plug, UInt32 oldVal, UInt32 newVal) { //syslog(LOG_INFO, "Plug %d written to, old val 0x%x new val 0x%x\n", plug, oldVal, newVal); } static IOReturn writeDeviceOutputMCR(IOFireWireLibDeviceRef interface, UInt32 mask, UInt32 val) { UInt32 oldVal, newVal; IOReturn err; FWAddress addr; io_object_t obj; addr.nodeID = 0; addr.addressHi = 0xffff; addr.addressLo = 0xf0000900; obj = (*interface)->GetDevice(interface); err = (*interface)->ReadQuadlet(interface, obj, &addr, &oldVal, false, 0); if(err == kIOReturnSuccess) { if( (oldVal & mask) != val) { newVal = (oldVal & ~mask) | val; err = (*interface)->CompareSwap(interface, obj, &addr, oldVal, newVal, false, 0); } } return err; } static IOReturn MakeP2PConnectionForWrite(DVDevice *pDVDevice,UInt32 plug, UInt32 chan) { IOReturn err; err = (*pDVDevice->fAVCInterface)->makeP2PInputConnection(pDVDevice->fAVCInterface, plug, chan); if (err == kIOReturnSuccess) { pDVDevice->p2pConnected = kWriteP2PConnection; pDVDevice->p2pPlug = plug; pDVDevice->p2pChan = chan; } return err; } static IOReturn BreakP2PConnectionForWrite(DVDevice *pDVDevice,UInt32 plug, UInt32 chan) { IOReturn err; err = (*pDVDevice->fAVCInterface)->breakP2PInputConnection(pDVDevice->fAVCInterface, plug); // Always clear the connected flag, even if there was an error. pDVDevice->p2pConnected = kNoP2PConnection; return err; } static IOReturn MakeP2PConnectionForRead(DVDevice *pDVDevice,UInt32 plug, UInt32 chan) { IOReturn err; err = (*pDVDevice->fAVCInterface)->makeP2POutputConnection(pDVDevice->fAVCInterface, plug,chan,kFWSpeedInvalid); if (err == kIOReturnSuccess) { pDVDevice->p2pConnected = kReadP2PConnection; pDVDevice->p2pPlug = plug; pDVDevice->p2pChan = chan; } return err; } static IOReturn BreakP2PConnectionForRead(DVDevice *pDVDevice,UInt32 plug, UInt32 chan) { IOReturn err; err = (*pDVDevice->fAVCInterface)->breakP2POutputConnection(pDVDevice->fAVCInterface, plug); // Always clear the connected flag, even if there was an error. pDVDevice->p2pConnected = kNoP2PConnection; return err; } void AVCUnitMessageCallback(void * refCon, UInt32 type, void * arg ) { DVDevice *pDVDevice = (DVDevice*) refCon; // If this is a bus-reset notification, see if we have a p2p connection. // If so, restore the P2P connection, do on real time thread for safety. // Done by kernel now. // Callback the client's message notification handler if (pDVDevice->fThread->fDeviceMessage != NULL) pDVDevice->fThread->fDeviceMessage((void*)pDVDevice->deviceIndex,type,arg); return; } static IOReturn getSignalMode(IOFireWireAVCLibUnitInterface **avc, UInt8 *mode) { UInt32 size; UInt8 cmd[4],response[4]; IOReturn res; // build query Output Signal Mode command cmd[0] = kAVCStatusInquiryCommand; cmd[1] = IOAVCAddress(kAVCTapeRecorder, 0); cmd[2] = kAVCOutputSignalModeOpcode; cmd[3] = kAVCSignalModeDummyOperand; size = 4; res = (*avc)->AVCCommand(avc, cmd, 4, response, &size); if(res == kIOReturnSuccess) { *mode = response[3]; } return res; } static bool isDVCPro(IOFireWireAVCLibUnitInterface **avc, UInt8 *pMode) { UInt32 size; UInt8 cmd[10],response[10]; IOReturn res; // build query vender-dependent command (is DVCPro?). cmd[0] = kAVCStatusInquiryCommand; cmd[1] = kAVCUnitAddress; cmd[2] = kAVCVendorDependentOpcode; cmd[3] = 0; cmd[4] = 0x80; cmd[5] = 0x45; cmd[6] = 0x82; cmd[7] = 0x48; cmd[8] = 0xff; cmd[9] = 0xff; size = 10; res = (*avc)->AVCCommand(avc, cmd, 10, response, &size); // If it is DVCPro50, see if its 25 or 50 if ((res == kIOReturnSuccess) && (response[0] == kAVCImplementedStatus)) { cmd[0] = kAVCStatusInquiryCommand; cmd[1] = kAVCUnitAddress; cmd[2] = kAVCOutputPlugSignalFormatOpcode; cmd[3] = 0; cmd[4] = 0xFF; cmd[5] = 0xFF; cmd[6] = 0xFF; cmd[7] = 0xFF; size = 8; res = (*avc)->AVCCommand(avc, cmd, 8, response, &size); if (res == kIOReturnSuccess && response[0] == kAVCImplementedStatus) *pMode = response[5]; else *pMode = 0x00; return true; } else return false; } static bool isSDL(IOFireWireAVCLibUnitInterface **avc, UInt8 signalMode) { // Try setting input mode to SDL IOReturn res; bool hasSDL; UInt32 size; UInt8 cmd[4],response[4]; cmd[0] = kAVCControlCommand; cmd[1] = IOAVCAddress(kAVCTapeRecorder, 0); cmd[2] = kAVCInputSignalModeOpcode; cmd[3] = (signalMode & ~kAVCSignalModeMask_STYPE) | kAVCSignalModeMask_SDL; size = 4; res = (*avc)->AVCCommand(avc, cmd, 4, response, &size); if(res != kIOReturnSuccess || response[0] != kAVCAcceptedStatus) return false; // Failed to set to SDL cmd[0] = kAVCStatusInquiryCommand; cmd[1] = IOAVCAddress(kAVCTapeRecorder, 0); cmd[2] = kAVCInputSignalModeOpcode; cmd[3] = kAVCSignalModeDummyOperand; size = 4; res = (*avc)->AVCCommand(avc, cmd, 4, response, &size); hasSDL = (response[3] & kAVCSignalModeMask_STYPE) == kAVCSignalModeMask_SDL; // Set back to original value cmd[0] = kAVCControlCommand; cmd[1] = IOAVCAddress(kAVCTapeRecorder, 0); cmd[2] = kAVCInputSignalModeOpcode; cmd[3] = signalMode; size = 4; res = (*avc)->AVCCommand(avc, cmd, 4, response, &size); return hasSDL; } static void deviceArrived(void *refcon, io_iterator_t iterator ) { io_object_t obj; DVThread * dvThread = (DVThread *)refcon; UInt8 dvcProMode; //syslog(LOG_INFO,"deviceArrived(0x%x, 0x%x)\n", refcon, iterator); while(obj = IOIteratorNext(iterator)) { CFMutableDictionaryRef properties; CFNumberRef dataDesc; CFStringRef strDesc; kern_return_t err; UInt64 GUID; int refound = 0; int device; DVDevice *dev = NULL; //syslog(LOG_INFO, "object 0x%x arrived!\n", obj); err = IORegistryEntryCreateCFProperties(obj, &properties, kCFAllocatorDefault, kNilOptions); dataDesc = (CFNumberRef)CFDictionaryGetValue(properties, CFSTR("GUID")); CFNumberGetValue(dataDesc, kCFNumberSInt64Type, &GUID); for(device=0; devicefNumDevices; device++) { if(GUID == dvThread->fDevices[device].fGUID) { refound = 1; dev = &dvThread->fDevices[device]; break; } } if(!refound) { CFBooleanRef hasFCP; device = dvThread->fNumDevices; dvThread->fNumDevices++; dev = &dvThread->fDevices[device]; strDesc = (CFStringRef)CFDictionaryGetValue(properties, CFSTR("FireWire Product Name")); if(strDesc) { dev->fName[0] = 0; CFStringGetCString(strDesc, dev->fName, sizeof(dev->fName), kCFStringEncodingMacRoman); } hasFCP = (CFBooleanRef)CFDictionaryGetValue(properties, CFSTR("supportsFCP")); dev->fSupportsFCP = true; if(hasFCP) dev->fSupportsFCP = CFBooleanGetValue(hasFCP); dev->fGUID = GUID; dev->fMaxSpeed = kFWSpeed100MBit; dev->fWriteChan = kWriteChannel; dev->fReadChan = kReadChannel; } CFRelease(properties); dev->fObject = obj; dev->fThread = dvThread; // Request notification of messages via AVC user client err = openAVCUnit(dev->fObject, &dev->fAVCInterface, dvThread); if(err == kIOReturnSuccess) { UInt8 mode, stype; dev->deviceIndex = device+1; (*dev->fAVCInterface)->setMessageCallback(dev->fAVCInterface, (void *) dev, AVCUnitMessageCallback); // Determine mode(s) supported if(dev->fSupportsFCP) { err = getSignalMode(dev->fAVCInterface, &mode); if(err == kIOReturnSuccess) { if(mode & kAVCSignalModeMask_50) dev->standard = palIn; else dev->standard = ntscIn; // See if DVCPro25 type device stype = mode & kAVCSignalModeMask_STYPE; if(stype == kAVCSignalModeMask_DVCPro25) { dev->fDVFormats |= 1 << kIDHDVCPro_25; } else if(stype == kAVCSignalModeMask_DVCPro50) { dev->fDVFormats |= 1 << kIDHDVCPro_50; dev->fMaxSpeed = kFWSpeed400MBit; // Default to 400 for DVCPro-50 } else { // Ask device via vender-dependent command if it's a DVCPro device. if(isDVCPro(dev->fAVCInterface,&dvcProMode)) { if((dvcProMode & kAVCSignalModeMask_STYPE) == kAVCSignalModeMask_DVCPro50) { dev->fDVFormats |= 1 << kIDHDVCPro_50; dev->fMaxSpeed = kFWSpeed400MBit; // Default to 400 for DVCPro-50 } else dev->fDVFormats |= 1 << kIDHDVCPro_25; } } // See if SDL type device if(stype == kAVCSignalModeMask_SDL) dev->fDVFormats |= 1 << kIDHDV_SDL; else { // Ask camera if it's SDL. if(isSDL(dev->fAVCInterface, mode)) dev->fDVFormats |= 1 << kIDHDV_SDL; } } else { // Failed the signal mode command. Assume standard NTSC DV dev->fDVFormats = 1 << kIDHDV_SD; // Standard DV dev->standard = ntscIn; } } else { // Assume NTSC, standard DV if device doesn't support AVC. dev->fDVFormats = 1 << kIDHDV_SD; // Standard DV dev->standard = ntscIn; // device standard - NTSC/PAL } // Notify client (dvThread->fAddedFunc)(dvThread->fAddedRefCon, dev, device+1, refound); } } } static OSStatus DVthreadExit(DVThread *dvThread, UInt32 params) { if(dvThread->fNotifySource) CFRunLoopSourceInvalidate(dvThread->fNotifySource); if(dvThread->fPowerNotifySource) CFRunLoopSourceInvalidate(dvThread->fPowerNotifySource); // we have to do this because CF sometimes adds it's own source to our run loop (?!) // which we don't (can't?) invalidate // this makes sure our thread will really exit.. CFRunLoopStop(dvThread->fWorkLoop) ; dvThread->fTimerFunc = NULL; return noErr; } static void *DVRTThreadStart(DVThread *dvThread) { ReceiveMsg msg; kern_return_t err; int delay; int run = true; int i; deviceArrived(dvThread, dvThread->fMatchEnumer); // signal that we're about to start the mach loop DVSignalSync(&dvThread->fRequestSyncer, &dvThread->fSyncRequest, 1); delay = 12; // DCL block size while(run) { int nextTick; #if TIMING CFAbsoluteTime start; start = CFAbsoluteTimeGetCurrent(); #endif err = mach_msg(&msg.msgHdr, MACH_RCV_MSG | MACH_RCV_TIMEOUT, //err = mach_msg(&msg.msgHdr, MACH_RCV_MSG | MACH_RCV_TIMEOUT | MACH_RCV_LARGE, 0, sizeof(msg), dvThread->fRequestMachPort, delay, MACH_PORT_NULL); #if TIMING DVLog(dvThread, 'mmsg', start, CFAbsoluteTimeGetCurrent()); #endif if(err == MACH_MSG_SUCCESS) { switch (msg.msgHdr.msgh_id) { case kDVRequestID: dvThread->fRequestResult = (dvThread->fRequestFunc)(dvThread->fRequestArg, dvThread->fRequestParam); if(dvThread->fRequestFunc == DVthreadExit) run = false; DVSignalSync(&dvThread->fRequestSyncer, &dvThread->fSyncRequest, (UInt32)dvThread->fRequestFunc); } } for(i=0; ifInStreams[i]) DVReadPoll(dvThread->fInStreams[i]); if(dvThread->fOutStreams[i]) DVWritePoll(dvThread->fOutStreams[i]); } if(dvThread->fTimerFunc) { dvThread->fTimerFunc(NULL, dvThread->fTimerRefCon); delay = 12; // DCL block size in milliseconds nextTick = (int)((dvThread->requestTimeoutTime-CFAbsoluteTimeGetCurrent())*1000.0); if(nextTick <= 0) nextTick = 1; if(nextTick < delay) delay = nextTick; } } return NULL; } static void *DVRLThreadStart(DVThread *thread) { CFRunLoopRef loop; //syslog(LOG_INFO, "Starting thread: %p\n", thread); loop = CFRunLoopGetCurrent(); //printf("Starting thread: %p, loop %p, notify retain %d, notify %p ioport %p, info %x\n", // thread, loop, retain, thread->fNotifySource, thread->fNotifyPort, *((UInt32 *)thread->fNotifySource + 1)); if(thread->fNotifySource) CFRunLoopAddSource(loop, thread->fNotifySource, kCFRunLoopDefaultMode); if(thread->fPowerNotifySource) CFRunLoopAddSource(loop, thread->fPowerNotifySource, kCFRunLoopDefaultMode); CFRetain(loop); thread->fWorkLoop = loop; // signal that we're about to start the runloop DVSignalSync(&thread->fRequestSyncer, &thread->fSyncRequest, 1); CFRunLoopRun(); //printf("Exiting thread: %p, loop %p\n", thread, loop); return NULL; } void PowerManagementNotificationCallback(void * refcon, io_service_t service, natural_t messageType, void * messageArgument ) { DVThread *dvThread = (DVThread*) refcon; UInt32 i; // If we are waking from sleep, restart any running streams if (messageType == kIOMessageSystemHasPoweredOn) { // Find all active streams, and restart them for(i=0; ifInStreams[i]) { syslog(LOG_INFO, "DV PowerManagementNotificationCallback, Restarting input stream %d\n",i); if (dvThread->fInStreams[i]->dvReadStopInProgress == false) { dvThread->fInStreams[i]->pendingDVReadUnderrunHandler = true; DVRequest(dvThread->fInStreams[i]->fStreamVars.fThread, doDVReadHandleInputUnderrun, dvThread->fInStreams[i], 0); } } if(dvThread->fOutStreams[i]) { syslog(LOG_INFO, "DV PowerManagementNotificationCallback, Restarting output stream %d\n",i); if (dvThread->fOutStreams[i]->dvWriteStopInProgress == false) { dvThread->fOutStreams[i]->pendingDVWriteUnderrunHandler = true; DVRequest(dvThread->fOutStreams[i]->fStreamVars.fThread, doDVHandleOutputUnderrun, dvThread->fOutStreams[i], 0); } } } } // Acknowledge the message IOAllowPowerChange (dvThread->fPowerNotifyConnect, (long) messageArgument); } DVThread * DVCreateThread(DVDeviceArrivedFunc deviceAdded, void * addedRefCon, CFRunLoopTimerCallBack timerTick, void *timerRefCon, IOFWAVCMessageCallback deviceMessage) { UInt32 i; IOReturn err; mach_port_t masterDevicePort; DVThread *dvThread; const UInt8 num = kAVCTapeRecorder; CFMutableDictionaryRef dict = 0; CFNumberRef tape; dict = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if(!dict) return nil; tape = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt8Type, &num); if(!tape) return nil; CFDictionarySetValue( dict, CFSTR(kIOProviderClassKey), CFSTR("IOFireWireAVCSubUnit") ); CFDictionarySetValue( dict, CFSTR("SubUnit_Type"), tape); CFRelease(tape); if ((err = IOMasterPort(bootstrap_port, &masterDevicePort)) != KERN_SUCCESS) { return NULL; } dvThread = malloc(sizeof(DVThread)); bzero(dvThread, sizeof(DVThread)); for(i = 0 ; i < kDVMaxDevicesActive ; i++){ dvThread->fDevices[i].fOutPlug = kNoPlug; } pthread_mutex_init(&dvThread->fRequestSyncer.fMutex, NULL); pthread_cond_init(&dvThread->fRequestSyncer.fSyncCond, NULL); pthread_mutex_init(&dvThread->fRequestMutex, NULL); dvThread->fNotifyPort = IONotificationPortCreate(masterDevicePort); dvThread->fNotifySource = IONotificationPortGetRunLoopSource(dvThread->fNotifyPort); err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &dvThread->fRequestMachPort); err = mach_port_insert_right(mach_task_self(), dvThread->fRequestMachPort, dvThread->fRequestMachPort, MACH_MSG_TYPE_MAKE_SEND); if(timerTick) { dvThread->fTimerFunc = timerTick; dvThread->fTimerRefCon = timerRefCon; } dvThread->fAddedRefCon = addedRefCon; dvThread->fAddedFunc = deviceAdded; dvThread->fDeviceMessage = deviceMessage; err = IOServiceAddMatchingNotification( dvThread->fNotifyPort, kIOMatchedNotification, dict, deviceArrived, dvThread, &dvThread->fMatchEnumer ); // Register for system power notifications dvThread->fPowerNotifyConnect = IORegisterForSystemPower ( dvThread, &dvThread->fPowerNotifyPort, PowerManagementNotificationCallback, &dvThread->fPowerManagementNotifier); dvThread->fPowerNotifySource = IONotificationPortGetRunLoopSource(dvThread->fPowerNotifyPort); return dvThread; } static void setThreadPriority(pthread_t thread) { double mult; unsigned int delta; unsigned int abs_to_ns_num; unsigned int abs_to_ns_denom; unsigned int proc_to_abs_num; unsigned int proc_to_abs_denom; thread_time_constraint_policy_data_t constraints; kern_return_t result; // Set thread to Real Time #if 0 { mach_msg_type_number_t count; boolean_t get_default = TRUE; count = THREAD_TIME_CONSTRAINT_POLICY_COUNT; thread_policy_get(pthread_mach_thread_np(thread), THREAD_TIME_CONSTRAINT_POLICY, (thread_policy_t)&constraints, &count, &get_default); syslog(LOG_INFO, "default period %d computation %d constraint %d preemptible %d\n", constraints.period, constraints.computation, constraints.constraint, constraints.preemptible); } #endif (void)MKGetTimeBaseInfo (&delta, &abs_to_ns_num, &abs_to_ns_denom, &proc_to_abs_num, &proc_to_abs_denom); mult = ((double)abs_to_ns_denom / (double)abs_to_ns_num) * 1000000; constraints.period = 12*mult; constraints.computation = 2*mult; constraints.constraint = 24*mult; constraints.preemptible = TRUE; result = thread_policy_set(pthread_mach_thread_np(thread), THREAD_TIME_CONSTRAINT_POLICY, (thread_policy_t)&constraints, THREAD_TIME_CONSTRAINT_POLICY_COUNT); //syslog(LOG_INFO, "result %x, our period %d computation %d constraint %d preemptible %d\n", // result, constraints.period, constraints.computation, constraints.constraint, constraints.preemptible); } void DVSetTimeoutTime(DVThread * dvThread, CFAbsoluteTime fireDate) { dvThread->setTimeoutTime = CFAbsoluteTimeGetCurrent(); dvThread->requestTimeoutTime = fireDate; } void DVRunThread(DVThread * dvThread) { pthread_attr_t threadAttr; // Attributes of work thread pthread_t thread; // Start each thread, wait for first to start before setting up second. dvThread->fSyncRequest = 0; pthread_attr_init(&threadAttr); //pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED); pthread_create(&thread, &threadAttr, DVRLThreadStart, dvThread); dvThread->fRLThread = thread; DVWaitSync(&dvThread->fRequestSyncer, &dvThread->fSyncRequest); dvThread->fSyncRequest = 0; pthread_create(&thread, &threadAttr, DVRTThreadStart, dvThread); dvThread->fRTThread = thread; setThreadPriority(thread); DVWaitSync(&dvThread->fRequestSyncer, &dvThread->fSyncRequest); } void DVFreeThread(DVThread * dvThread) { DVRequest(dvThread, DVthreadExit, dvThread, 0); pthread_join(dvThread->fRTThread, NULL); pthread_join(dvThread->fRLThread, NULL); //printf("Workloop retain %d\n", CFGetRetainCount(dvThread->fWorkLoop)); CFRelease(dvThread->fWorkLoop); //CFRunLoopRemoveSource(dvThread->fWorkLoop, dvThread->fNotifySource, kCFRunLoopDefaultMode); //CFRunLoopRemoveSource(dvThread->fWorkLoop, dvThread->fRequestSource, kCFRunLoopDefaultMode); //printf("after thread exit, notify , request retains are %d, %d\n", // CFGetRetainCount(dvThread->fNotifySource), CFGetRetainCount(dvThread->fRequestSource)); if(dvThread->fMatchEnumer) IOObjectRelease(dvThread->fMatchEnumer); if(dvThread->fNotifyPort) { CFMachPortRef hack; CFMachPortContext context; Boolean shouldFreeInfo; context.version = 1; context.info = (void *) dvThread->fNotifyPort; context.retain = NULL; context.release = NULL; context.copyDescription = NULL; hack = CFMachPortCreateWithPort(NULL, IONotificationPortGetMachPort(dvThread->fNotifyPort), NULL, &context, &shouldFreeInfo); CFMachPortInvalidate(hack); IONotificationPortDestroy(dvThread->fNotifyPort); //printf("hack port retain %d\n", CFGetRetainCount(hack)); CFRelease(hack); } IONotificationPortDestroy(dvThread->fPowerNotifyPort); mach_port_destroy(mach_task_self(), dvThread->fRequestMachPort); //printf("after IONotificationPortDestroy, notify retain is %d\n", CFGetRetainCount(dvThread->fNotifySource)); pthread_mutex_destroy(&dvThread->fRequestSyncer.fMutex); pthread_cond_destroy(&dvThread->fRequestSyncer.fSyncCond); pthread_mutex_destroy(&dvThread->fRequestMutex); IODeregisterForSystemPower(&dvThread->fPowerManagementNotifier); memset(dvThread, 0xde, sizeof(DVThread)); free(dvThread); } void DVSignalSync(ThreadSyncer *sync, UInt32 *var, UInt32 val) { pthread_mutex_lock(&sync->fMutex); *var = val; pthread_mutex_unlock(&sync->fMutex); pthread_cond_broadcast(&sync->fSyncCond); } void DVWaitSync(ThreadSyncer *sync, UInt32 *var) { //if(!*var) { pthread_mutex_lock(&sync->fMutex); while(!*var) { pthread_cond_wait(&sync->fSyncCond, &sync->fMutex); } pthread_mutex_unlock(&sync->fMutex); } } void DVLock(ThreadSyncer *sync) { pthread_mutex_lock(&sync->fMutex); } void DVUnlock(ThreadSyncer *sync) { pthread_mutex_unlock(&sync->fMutex); } static IOReturn isochPortGetSupported( IOFireWireLibIsochPortRef interface, IOFWSpeed* outMaxSpeed, UInt64* outChanSupported) { DVStream *stream; stream = (DVStream *)((*interface)->GetRefCon(interface)); if(*outMaxSpeed > stream->fMaxSpeed) *outMaxSpeed = stream->fMaxSpeed; *outChanSupported = stream->fChannelMask; return kIOReturnSuccess; } static IOReturn isochPortAllocate( IOFireWireLibIsochPortRef interface, IOFWSpeed maxSpeed, UInt32 channel) { DVStream *stream; stream = (DVStream *)((*interface)->GetRefCon(interface)); //printf("using channel %d\n", channel); stream->fIsocChannel = channel; return kIOReturnSuccess; } IOReturn openFireWireUnit(IOFireWireAVCLibUnitInterface **avcInterface, IOFireWireSessionRef session, IOFireWireLibDeviceRef *retInterface, DVThread *thread) { IOFireWireLibDeviceRef resultInterface; IOReturn err = kIOReturnNoMemory; int opened = false; do { resultInterface = (*avcInterface)->getAncestorInterface(avcInterface, "IOFireWireUnit", CFUUIDGetUUIDBytes(kIOFireWireLibTypeID), CFUUIDGetUUIDBytes(kIOFireWireUnitInterfaceID_v3)); if(!resultInterface) break; if(session) err = (*resultInterface)->OpenWithSessionRef(resultInterface, session); else err = (*resultInterface)->Open(resultInterface); if(err) break; opened = true; //err = (*resultInterface)->AddCallbackDispatcherToRunLoop(resultInterface, workLoop ); err = (*resultInterface)->AddIsochCallbackDispatcherToRunLoop(resultInterface, thread->fWorkLoop); } while (false); if(!err) *retInterface = resultInterface; else { if(opened) (*resultInterface)->Close(resultInterface); if(resultInterface) (*resultInterface)->Release(resultInterface); } return err; } IOReturn openAVCUnit(io_object_t obj, IOFireWireAVCLibUnitInterface ***retInterface, DVThread *thread) { IOCFPlugInInterface** theCFPlugInInterface; IOFireWireAVCLibUnitInterface **resultInterface = 0 ; SInt32 theScore ; IOReturn err; err = IOCreatePlugInInterfaceForService( obj, kIOFireWireAVCLibUnitTypeID, kIOCFPlugInInterfaceID, //interfaceType, & theCFPlugInInterface, & theScore); if (!err) { HRESULT comErr; comErr = (*theCFPlugInInterface)->QueryInterface( theCFPlugInInterface, CFUUIDGetUUIDBytes(kIOFireWireAVCLibUnitInterfaceID), (void**) & resultInterface); if (comErr == S_OK) { err = (*resultInterface)->addCallbackDispatcherToRunLoop(resultInterface, thread->fWorkLoop ); } else err = comErr; (*theCFPlugInInterface)->Release(theCFPlugInInterface); // Leave just one reference. } if(!err) *retInterface = resultInterface; return err; } IOReturn openAVCProto(IOFireWireAVCLibUnitInterface **avcInterface, IOFireWireAVCLibProtocolInterface ***retInterface, DVThread *thread) { IOFireWireAVCLibProtocolInterface **resultInterface; IOReturn err = noErr; do { resultInterface = (*avcInterface)->getProtocolInterface(avcInterface, CFUUIDGetUUIDBytes(kIOFireWireAVCLibProtocolTypeID), CFUUIDGetUUIDBytes(kIOFireWireAVCLibProtocolInterfaceID)); if(!resultInterface) break; err = (*resultInterface)->addCallbackDispatcherToRunLoop(resultInterface, thread->fWorkLoop); } while (false); if(!err) *retInterface = resultInterface; else { if(resultInterface) (*resultInterface)->Release(resultInterface); } return err; } void DVDeviceTerminate(DVDevice *dev) { DVDeviceClose(dev); if(dev->fAVCInterface) { (*dev->fAVCInterface)->Release(dev->fAVCInterface); dev->fAVCInterface = NULL; } if(dev->fObject) { IOObjectRelease(dev->fObject); dev->fObject = NULL; } } IOReturn DVDeviceOpen(DVThread *dvThread, DVDevice *device) { IOReturn err = noErr; if(!device->fAVCInterface) return kIOReturnNoMemory; do { err = (*device->fAVCInterface)->open(device->fAVCInterface); if(err != kIOReturnSuccess) break; err = openFireWireUnit(device->fAVCInterface, (*device->fAVCInterface)->getSessionRef(device->fAVCInterface), &device->fDevInterface, dvThread); if(err != kIOReturnSuccess) break; err = openAVCProto(device->fAVCInterface, &device->fAVCProtoInterface, dvThread); if(err != kIOReturnSuccess) break; err = (*device->fAVCProtoInterface)->allocateOutputPlug(device->fAVCProtoInterface, device, handlePCRLock, &device->fOutPlug); if(err != kIOReturnSuccess) break; err = writePlug(device->fAVCProtoInterface, device->fOutPlug, 122 << kIOFWPCROutputPayloadPhase); if(err != kIOReturnSuccess) break; } while (0); if(err != kIOReturnSuccess) DVDeviceClose(device); return err; } static IOReturn doDVDeviceClose(DVDevice *dev) { if(dev->fDevInterface) { UInt32 ref; (*dev->fDevInterface)->Close(dev->fDevInterface); ref = (*dev->fDevInterface)->Release(dev->fDevInterface); //syslog(LOG_INFO, "DVCloseDriver FW refcount was %d\n", ref); dev->fDevInterface = NULL; } if(dev->fAVCProtoInterface) { UInt32 ref; if(dev->fOutPlug != kNoPlug) { (*dev->fAVCProtoInterface)->freeOutputPlug(dev->fAVCProtoInterface, dev->fOutPlug); dev->fOutPlug = kNoPlug; } ref = (*dev->fAVCProtoInterface)->Release(dev->fAVCProtoInterface); //syslog(LOG_INFO, "DVCloseDriver AVCproto refcount was %d\n", ref); dev->fAVCProtoInterface = NULL; } if(dev->fAVCInterface) { (*dev->fAVCInterface)->close(dev->fAVCInterface); } return kIOReturnSuccess; } void DVDeviceClose(DVDevice *dev) { DVRequest(dev->fThread, doDVDeviceClose, dev, 0); } IOReturn DVRequest(DVThread *thread, IOReturn (*func)(void *arg, UInt32 param), void *arg, UInt32 param) { IOReturn result; //printf("Doing request %p\n", func); if(thread->fRTThread != pthread_self()) { pthread_mutex_lock(&thread->fRequestMutex); thread->fSyncRequest = 0; thread->fRequestFunc = func; thread->fRequestArg = arg; thread->fRequestParam = param; { SendMsg msg; bzero( &msg, sizeof(msg)); msg.msgHdr.msgh_remote_port = thread->fRequestMachPort; msg.msgHdr.msgh_bits = MACH_MSGH_BITS( MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_COPY_SEND ); msg.msgHdr.msgh_size = sizeof(msg); msg.msgHdr.msgh_id = kDVRequestID; mach_msg(&msg.msgHdr, MACH_SEND_MSG, msg.msgHdr.msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL); } DVWaitSync(&thread->fRequestSyncer, &thread->fSyncRequest); result = thread->fRequestResult; pthread_mutex_unlock(&thread->fRequestMutex); } else result = (*func)(arg, param); return result; } static void initStream(DVStream *stream, DVDevice *device, UInt32 plug, UInt32 channel, DVThread *thread) { stream->pFWDevice = device->fDevInterface; stream->pDVDevice = device; stream->fAVCProtoInterface = device->fAVCProtoInterface; stream->fPlug = plug; stream->fIsocChannel = channel; stream->fMaxSpeed = device->fMaxSpeed; stream->fThread = thread; } static IOReturn openStream(DVStream *stream, bool forWrite, UInt32 packetSize) { IOReturn err; IOFireWireLibIsochPortRef talker, listener; IOVirtualRange bufRange; bool allocBandwidth; do { if(forWrite) { // always allocate bandwidth allocBandwidth = true; } else { // Figure out if the device is already tranmitting, in which case use that channel and don't // allocate bandwidth UInt32 plugVal; io_object_t obj; FWAddress addr; UInt32 size; // Use any channel not already in use, or the channel that the camcorder is already using addr.nodeID = 0; addr.addressHi = 0xffff; addr.addressLo = 0xf0000904; size = 4; obj = (*stream->pFWDevice)->GetDevice(stream->pFWDevice); err = (*stream->pFWDevice)->ReadQuadlet(stream->pFWDevice, obj, &addr, &plugVal, false, 0); if(plugVal & (kIOFWPCRBroadcast | kIOFWPCRP2PCount)) { UInt32 chan = (plugVal & kIOFWPCRChannel)>>kIOFWPCRChannelPhase; //printf("Already transmitting on channel %x\n", chan); stream->fChannelMask = 1ULL << (63-chan); allocBandwidth = false; } else { #ifdef USE_P2P_CONNECTIONS_FOR_DV_READ stream->fChannelMask = ~1ULL; allocBandwidth = true; #else stream->fChannelMask = 1ULL; // Assume the camera will use channel 63 allocBandwidth = false; #endif } } stream->fIsochChannelRef = (*stream->pFWDevice)->CreateIsochChannel(stream->pFWDevice, allocBandwidth, packetSize, stream->fMaxSpeed, CFUUIDGetUUIDBytes(kIOFireWireIsochChannelInterfaceID)); if (NULL == stream->fIsochChannelRef) { err = memFullErr; break; } bufRange.address = (IOVirtualAddress)stream->fDCLBuffers; bufRange.length = stream->fDCLBufferSize; // Add local node as talker or listener if(forWrite) { stream->pFWLocalIsochPort = (*stream->pFWDevice)->CreateLocalIsochPort(stream->pFWDevice, 1 /*inTalking*/, stream->pDCLList, kFWDCLCycleEvent, 0, 0x0000f000, nil, 0, &bufRange, 1, CFUUIDGetUUIDBytes(kIOFireWireLocalIsochPortInterfaceID)); // Use any available channel stream->fChannelMask = ~1ULL; } else { stream->pFWLocalIsochPort = (*stream->pFWDevice)->CreateLocalIsochPort(stream->pFWDevice, 0 /*inTalking*/, stream->pDCLList, 0, 0, 0, nil, 0, &bufRange, 1, CFUUIDGetUUIDBytes(kIOFireWireLocalIsochPortInterfaceID)); } if (!stream->pFWLocalIsochPort) { err = memFullErr; break; } stream->pFWRemoteIsochPort = (*stream->pFWDevice)->CreateRemoteIsochPort(stream->pFWDevice, 0, CFUUIDGetUUIDBytes(kIOFireWireRemoteIsochPortInterfaceID) ); (*stream->pFWRemoteIsochPort)->SetRefCon( stream->pFWRemoteIsochPort, stream); (*stream->pFWRemoteIsochPort)->SetGetSupportedHandler( stream->pFWRemoteIsochPort, &isochPortGetSupported); (*stream->pFWRemoteIsochPort)->SetAllocatePortHandler( stream->pFWRemoteIsochPort, &isochPortAllocate); if(forWrite) { talker = (IOFireWireLibIsochPortRef) stream->pFWLocalIsochPort; listener = (IOFireWireLibIsochPortRef)stream->pFWRemoteIsochPort; } else { listener = (IOFireWireLibIsochPortRef) stream->pFWLocalIsochPort; talker = (IOFireWireLibIsochPortRef)stream->pFWRemoteIsochPort; } err = (*stream->fIsochChannelRef)->SetTalker( stream->fIsochChannelRef, talker); if(err) break; err = (*stream->fIsochChannelRef)->AddListener( stream->fIsochChannelRef, listener); if(err) break; // Initialize isochronous channel. (*stream->fIsochChannelRef)->TurnOnNotification(stream->fIsochChannelRef); err = (*stream->fIsochChannelRef)->AllocateChannel(stream->fIsochChannelRef); if(err) break; if(forWrite) { // set our output plug broadcast bit, channel number and bandwidth usage. err = writePlug(stream->fAVCProtoInterface, stream->fPlug, kIOFWPCROnline | kIOFWPCRBroadcast | (1 << kIOFWPCRP2PCountPhase) | (stream->fIsocChannel<pDVDevice,0,stream->fIsocChannel); } else { #ifdef USE_P2P_CONNECTIONS_FOR_DV_READ err = MakeP2PConnectionForRead(stream->pDVDevice,0,stream->fIsocChannel); #endif } err = (*stream->fIsochChannelRef)->Start(stream->fIsochChannelRef); if(err) break; stream->fFrames.fStatus = kDVRunning; } while (false); // If we got any errors, call closeStream now to cleanup if(err) closeStream(stream); return err; } static void closeStream(DVStream *stream) { IOReturn err; stream->fFrames.fStatus = kDVStopped; if(stream->fIsochChannelRef) { (*stream->fIsochChannelRef)->TurnOffNotification(stream->fIsochChannelRef); //syslog(LOG_INFO, "Stopping stream %p\n", stream); err = (*stream->fIsochChannelRef)->Stop(stream->fIsochChannelRef); //syslog(LOG_INFO, "Stopped stream, err %x\n", err); //syslog(LOG_INFO, "ReleaseChannel of stream %p\n", stream); err = (*stream->fIsochChannelRef)->ReleaseChannel(stream->fIsochChannelRef); //syslog(LOG_INFO, "releaseChannelled, err %x\n", err); //syslog(LOG_INFO, "Releaseing channel of stream %p\n", stream); (*stream->fIsochChannelRef)->Release(stream->fIsochChannelRef); stream->fIsochChannelRef = NULL; } if(stream->pFWLocalIsochPort) { (*stream->pFWLocalIsochPort)->Release(stream->pFWLocalIsochPort); stream->pFWLocalIsochPort = NULL; } if(stream->pFWRemoteIsochPort) { (*stream->pFWRemoteIsochPort)->Release(stream->pFWRemoteIsochPort); stream->pFWRemoteIsochPort = NULL; } // Run the runloop for .1 secs to pick up stray DCL callbacks // But we don't want to run the other runloop sources... //CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); } static IOReturn DVAllocFrames(DVFrameVars *pFrameData, UInt32 numFrames, UInt32 frameSize, DVFrameVars **frameVars, UInt8 **frames) { int i; pFrameData->fNumFrames = numFrames; pFrameData->fFrames = malloc(numFrames*frameSize); pFrameData->fReader = 0; pFrameData->fWriter = 0; pFrameData->fDroppedFrames = 0; pFrameData->fStatus = 0; for(i=0; ifFrames + i*frameSize; } *frameVars = pFrameData; return kIOReturnSuccess; } static void DVFreeFrames(DVFrameVars *pFrameData) { if(!pFrameData->fFrames) return; free(pFrameData->fFrames); pFrameData->fFrames = NULL; } static void DVGetNextFullOutputFrame(DVFrameVars *pFrameData, UInt8** ppFrame, UInt32 frameSize ) { if(NULL == *ppFrame) { *ppFrame = pFrameData->fFrames; } else { if (pFrameData->fReader + 1 < pFrameData->fWriter) { pFrameData->fReader++; } else { //syslog(LOG_INFO, "DVGetNextFullOutputFrame: dropping frame: reader %d writer %d dropped %d\n", //pFrameData->fReader, pFrameData->fWriter, //pFrameData->fDroppedFrames); pFrameData->fDroppedFrames++; } *ppFrame = pFrameData->fFrames + frameSize*(pFrameData->fReader % pFrameData->fNumFrames); } } void DVSetInputFrameSizeAndMode(DVFrameVars *pFrameData, UInt32 bytes, UInt8 mode, UInt32 frameTime ) { int index = pFrameData->fWriter % pFrameData->fNumFrames; int i; pFrameData->fFrameSize[index] = bytes; pFrameData->fFrameStandard[index] = mode; pFrameData->fFrameTime[index] = frameTime; pFrameData->fFrameStatus[index] = kReady; // find next free frame for(i=pFrameData->fWriter + 1; i < pFrameData->fReader + pFrameData->fNumFrames; i++) { if(pFrameData->fFrameStatus[i % pFrameData->fNumFrames] != kReading) break; //syslog(LOG_INFO, "Skipping frame %d (%d) state %d\n", // i, i % pFrameData->fNumFrames, pFrameData->fFrameStatus[i % pFrameData->fNumFrames]); } if (i< pFrameData->fReader + pFrameData->fNumFrames) pFrameData->fWriter = i; else { pFrameData->fDroppedFrames++; //(LOG_INFO, "Dropping frame on input, dropped %d @ %d\n", // pFrameData->fDroppedFrames, pFrameData->fWriter); } } void DVGetNextEmptyInputFrame(DVFrameVars *pFrameData, UInt8** ppFrame, UInt32 frameSize ) { int index = pFrameData->fWriter % pFrameData->fNumFrames; *ppFrame = pFrameData->fFrames + frameSize*index; pFrameData->fFrameStatus[index] = kWriting; } static UInt32 getEmptyPacketsPerGroup(DVGlobalOutPtr pGlobalData, UInt32 numDataPacketsPerPlayBufferGroup) { // Compute the number of data packets per empty packet. // If the frame rate is expressed as n/d, the number of data packets per buffer group // expressed as A, and the number of data packets per frame as C, then the number of // empty packets per buffer group B should be // // B = int (8000*d/n*A/C - A + 1) // B = A*((8000*d)/(n*c) - 1) + 1 // // in order to ensure that the frame rate may be maintained by periodically reducing // the number of empty packets in a buffer group by 1. // UInt32 numEmptyPacketsPerPlayBufferGroup; UInt32 A1, C1, d1, n1; A1 = numDataPacketsPerPlayBufferGroup; C1 = pGlobalData->numDataPacketsPerFrame; n1 = pGlobalData->playFrameRateNumerator; d1 = pGlobalData->playFrameRateDenominator; #if ALT_TIMING { UInt32 d2 = C1*n1; UInt32 n2 = (8000 * d1 * A1) - (d2 * A1) + d2; numEmptyPacketsPerPlayBufferGroup = n2 / d2; } #else numEmptyPacketsPerPlayBufferGroup = (8000 * d1 * A1 + (n1 * C1)/2) / (n1 * C1) - A1; #endif return numEmptyPacketsPerPlayBufferGroup; } static void FreeDCLCommandPool(DVGlobalOutPtr pGlobalData) { if( pGlobalData->fDCLCommandPool != NULL ) { free(pGlobalData->fDCLCommandPool); pGlobalData->fDCLCommandPool = NULL; } } static IOReturn AllocateDCLCommandPool(DVGlobalOutPtr pGlobalData, UInt32 total ) { UInt8 * pDCLCommandPool; // Allocate DCL command pool record. pDCLCommandPool = malloc(total); if (pDCLCommandPool == NULL) { // syslog(LOG_INFO, "AllocateDCLCommandPool: IOMalloc: pDCLCommandPool failed\n"); return kIOReturnNoMemory; } else { pGlobalData->fTotalPool = total; pGlobalData->fAllocatedPool = 0; pGlobalData->fDCLCommandPool = pDCLCommandPool; } return kIOReturnSuccess; } static DCLCommandPtr AllocateDCLCommand(DVGlobalOutPtr pGlobalData, UInt32 dclSize ) { DCLCommandPtr pDCLCommand; if(pGlobalData->fAllocatedPool + dclSize <= pGlobalData->fTotalPool) { pDCLCommand = (DCLCommandPtr)(pGlobalData->fDCLCommandPool + pGlobalData->fAllocatedPool); pGlobalData->fAllocatedPool += dclSize; } else { syslog(LOG_INFO, "Trying to allocated DCL command size %d, no space left\n", dclSize); pDCLCommand = NULL; } return (pDCLCommand); } static DVLocalOutPtr DVAllocatePlayBufferGroup(DVGlobalOutPtr pGlobalData, int num) { DVLocalOutPtr pLocalData, pPrevLocalData, pNextLocalData; // Allocate buffer group data record. pLocalData = &pGlobalData->fLocalDataArray[num]; pLocalData->pGlobalData = pGlobalData; pLocalData->fBlockNum = num; // Insert buffer group data record into list. if(num == 0) { pPrevLocalData = &pGlobalData->fLocalDataArray[kNumPlayBufferGroups-1]; } else pPrevLocalData = &pGlobalData->fLocalDataArray[num-1]; if(num == kNumPlayBufferGroups-1) pNextLocalData = &pGlobalData->fLocalDataArray[0]; else pNextLocalData = &pGlobalData->fLocalDataArray[num+1]; pLocalData->pNextLocalData = pNextLocalData; pLocalData->pPrevLocalData = pPrevLocalData; return (pLocalData); } static void DVDeallocatePlayBufferGroup( DVLocalOutPtr pLocalData ) { if ( pLocalData != NULL ) { if ( pLocalData->bufferGroupUpdateDCLList != NULL ) free(pLocalData->bufferGroupUpdateDCLList); } } static IOReturn DVCreatePlayBufferGroupUpdateList( DVLocalOutPtr pLocalData) { DCLCommandPtr pDCLCommand, pLastDCLCommand; DCLCommandPtr *updateDCLList, *pUpdateDCLListEntry; UInt32 opcode; UInt32 updateListSize; IOReturn error = 0; // Loop through all DCL commands in buffer group and count all send packet DCL // commands. pDCLCommand = pLocalData->pFirstBufferGroupDCLCommand; pLastDCLCommand = pLocalData->pLastBufferGroupDCLCommand; updateListSize = 0; while (pDCLCommand != pLastDCLCommand) { opcode = pDCLCommand->opcode & ~kFWDCLOpFlagMask; if ((opcode == kDCLSendPacketStartOp) || (opcode == kDCLSendPacketOp)) updateListSize++; pDCLCommand = pDCLCommand->pNextDCLCommand; } opcode = pDCLCommand->opcode & ~kFWDCLOpFlagMask; if ((opcode == kDCLSendPacketStartOp) || (opcode == kDCLSendPacketOp)) updateListSize++; // Allocate update list. updateDCLList = (DCLCommandPtr *)malloc( updateListSize * sizeof (DCLCommandPtr) ); if (updateDCLList == NULL) { // syslog(LOG_INFO, "DVCreatePlayBufferGroupUpdateList: IOMalloc: updateDCLList\n"); error = kIOReturnNoMemory; } else { bzero( updateDCLList, updateListSize * sizeof (DCLCommandPtr) ); } // Loop through all DCL commands in buffer group and add all send packet DCL // commands to update list. if (error == 0) { pDCLCommand = pLocalData->pFirstBufferGroupDCLCommand; pLastDCLCommand = pLocalData->pLastBufferGroupDCLCommand; pUpdateDCLListEntry = updateDCLList; while (pDCLCommand != pLastDCLCommand) { opcode = pDCLCommand->opcode & ~kFWDCLOpFlagMask; if ((opcode == kDCLSendPacketStartOp) || (opcode == kDCLSendPacketOp)) *pUpdateDCLListEntry++ = pDCLCommand; pDCLCommand = pDCLCommand->pNextDCLCommand; } opcode = pDCLCommand->opcode & ~kFWDCLOpFlagMask; if ((opcode == kDCLSendPacketStartOp) || (opcode == kDCLSendPacketOp)) *pUpdateDCLListEntry++ = pDCLCommand; } // Save update list. if (error == 0) { pLocalData->bufferGroupUpdateDCLList = updateDCLList; pLocalData->updateListSize = updateListSize; } else { pLocalData->bufferGroupUpdateDCLList = NULL; pLocalData->updateListSize = 0; } return ( error ); } static void ModifyDCLJump(IOFireWireLibLocalIsochPortRef port, DCLJumpPtr pDCLJump, DCLLabelPtr pDCLLabel) { // Send notification to DCL compiler. if (port) { (*port)->ModifyJumpDCL( port, pDCLJump, pDCLLabel); } } void DVSilenceFrame(UInt8 mode, UInt8* frame) { UInt32 i,j,k,n; UInt8 *tPtr; UInt8 sType = ((mode & 0x7C) >> 2); //syslog(LOG_INFO, "silencing frame %p\n", frame); // Get DSF flag in byte 3 of header (Blue Book p. 113) tPtr = frame; if ((tPtr[3] & 0x80) == 0) n=10; // ntsc else n=12; // pal if (sType == 1) n /= 2; // SDL else if (sType == 0x1D) n *= 2; // DVCPro-50 // Mute all the audio samples for (i=0;ipGlobalData; pCurrentDCLCommand = pLocalData->pFirstBufferGroupDCLCommand; nominalFrameCycleTime = pGlobalData->nominalFrameCycleTime; // Get data for previous buffer group. pPrevLocalData = pLocalData->pPrevLocalData; syt = pGlobalData->nextSYT; dbc = pGlobalData->nextDBC; dataPacketNum = pGlobalData->nextDataPacketNum; // Get local node ID. (*pGlobalData->fStreamVars.pFWDevice)->GetLocalNodeID(pGlobalData->fStreamVars.pFWDevice, &localNodeID); localNodeID &= 0x3f; shiftedNodeID = (UInt32)localNodeID << 24; // Get first send packet command for this buffer group. while (pCurrentDCLCommand->opcode != kDCLSendPacketStartOp) pCurrentDCLCommand = pCurrentDCLCommand->pNextDCLCommand; pDCLTransferPacket = (DCLTransferPacketPtr) pCurrentDCLCommand; // Update the packet buffers. numPackets = pLocalData->numPackets; if(pGlobalData->fUpdateBuffers) { // Get the next frame to output if( pGlobalData->pImageBuffer == NULL ) { DVGetNextFullOutputFrame(&pGlobalData->fStreamVars.fFrames, (UInt8 **)&(pGlobalData->pImageBuffer), pGlobalData->fStreamVars.fDVFrameSize); } } pImageBuffer = ( pGlobalData->pImageBuffer + (pGlobalData->fDataQuadSize * dataPacketNum) ); for( packetNum = 0; packetNum < numPackets; packetNum++) { // Set up packet header. pBuffer = (UInt32 *) pDCLTransferPacket->buffer; pBuffer[0] = pGlobalData->fHeader0 | (dbc & 0xFF) | shiftedNodeID; pBuffer[1] = pGlobalData->fHeader1 | 0xFFFF; // if not an empty packet if (pDCLTransferPacket->size > kDVPacketCIPSize) { // Set SYT field if this is the first data packet in the frame. if (dataPacketNum == 0) { pBuffer[1] = pGlobalData->fHeader1 | (syt & 0xFFFF); syt = AddFWCycleTimeToFWCycleTime(syt, pGlobalData->nominalFrameCycleTime); } // Copy data into packet. if(pGlobalData->fUpdateBuffers) { bcopy(pImageBuffer, (void *)((UInt32)(pDCLTransferPacket->buffer) + kDVPacketCIPSize), pGlobalData->fDataPacketSize); pImageBuffer += pGlobalData->fDataQuadSize; } // Increment dbc based on stream type // TODO: This will need to change to support 2x,4x modes on some signal types stype = pGlobalData->fStreamVars.fSignalMode & kAVCSignalModeMask_STYPE; switch (stype) { case kAVCSignalModeMask_DVCPro50: dbc += 2; // DBC increments by two for each packet break; case kAVCSignalModeMask_DVCPro100: dbc += 4; // DBC increments by four for each packet break; case kAVCSignalModeMask_SDL: case kAVCSignalModeMask_DVCPro25: case kAVCSignalModeMask_HD: default: // SD video stream dbc += 1; // DBC increments by one for each packet break; }; dataPacketNum++; // check if frame is done if (dataPacketNum == pGlobalData->numDataPacketsPerFrame ) { // syslog(LOG_INFO, "frame done\n"); // DVCIsochCompleteEvent theEvent; // post a DV event to let the curious know... // theEvent.eventHeader.deviceID = gpDVFWData->deviceID; // theEvent.eventHeader.theEvent = kDVIsochWriteComplete; // theEvent.pFrameBuffer = (Ptr) pImageBuffer; // theEvent.fwCycleTime = syt; // FIXME: DVCPostEvent( (DVCEventRecordPtr) &theEvent ); // syslog(LOG_INFO, "DVCPostEvent\n"); // pImageBuffer = (UInt32 *) pDVCDriverData->imageBuffer; dataPacketNum = 0; //pDVCDriverData->playData.imageBuffer = GetNextOutputFrame(); if(pGlobalData->fUpdateBuffers) { pLastImageBuffer = pGlobalData->pImageBuffer; DVGetNextFullOutputFrame(&pGlobalData->fStreamVars.fFrames, (UInt8 **)&(pGlobalData->pImageBuffer), pGlobalData->fStreamVars.fDVFrameSize); pImageBuffer = pGlobalData->pImageBuffer; // Mute the audio on repeating frames, based on repeating frame sequences if (pImageBuffer == pLastImageBuffer) DVSilenceFrame(pGlobalData->fStreamVars.fSignalMode, (UInt8 *)pImageBuffer); } // End if(pGlobalData->fUpdateBuffers) } // end if end frame } // end if empty // Find next send packet start command. pCurrentDCLCommand = pCurrentDCLCommand->pNextDCLCommand; while (pCurrentDCLCommand != NULL) { if (pCurrentDCLCommand->opcode != kDCLSendPacketStartOp) pCurrentDCLCommand = pCurrentDCLCommand->pNextDCLCommand; else break; } pDCLTransferPacket = (DCLTransferPacketPtr) pCurrentDCLCommand; } pGlobalData->nextSYT = syt; pGlobalData->nextDBC = dbc; pGlobalData->nextDataPacketNum = dataPacketNum; } static void DVHandleOutput(DVLocalOutPtr pLocalData) { DVGlobalOutPtr pGlobalData; DVLocalOutPtr pPrevLocalData; UInt32 nominalFrameCycleTime; UInt32 fractionalFrameCycleCount, fractionalFrameCycleOffset; SInt32 timeDrift; UInt32 cycleDrift; UInt32 projectedTimeStamp, projectedSYT; #if TIMING CFAbsoluteTime cstart, cend; cstart = CFAbsoluteTimeGetCurrent(); #endif //syslog(LOG_INFO, "DVHandleOutput: 0x%x\n", pDCLCommandPtr); pPrevLocalData = pLocalData->pPrevLocalData; pGlobalData = pLocalData->pGlobalData; nominalFrameCycleTime = pGlobalData->nominalFrameCycleTime; // Undo skipping empty packet if we're currently skipping a packet. if (pLocalData->skippingEmptyPacket) { ModifyDCLJump(pGlobalData->fStreamVars.pFWLocalIsochPort, pLocalData->pBufferGroupSkipEmptyPacketDCLJump, pLocalData->pBufferGroupDontSkipEmptyPacketDCLLabel); pGlobalData->activePackets++; pLocalData->skippingEmptyPacket = false; } // Compute time drift. // Compute the projected time stamp value for the first packet of the current // buffer group the next time this proc is called for the current buffer group. // Start at time stamp of first packet in next buffer group to be sent. projectedTimeStamp = *pLocalData->pBufferGroupTimeStampPtr; // Time last packet in group was sent last time round projectedTimeStamp = AddFWCycleTimeToFWCycleTime(projectedTimeStamp, 1 << 12); // Add the total number of cycles for all active buffer group packets. projectedTimeStamp = AddFWCycleTimeToFWCycleTime(projectedTimeStamp, pGlobalData->activePackets << 12); // Subtract the number of cycles for all packets in the current buffer group. projectedTimeStamp = SubtractFWCycleTimeFromFWCycleTime(projectedTimeStamp, pLocalData->numPackets << 12); // Compute the projected SYT value for the first packet of the current buffer group // the next time this proc is called for the current buffer group. // Start with the SYT value to use for the first packet of the next frame. projectedSYT = pGlobalData->nextSYT; // Subtract the SYT offset between frames if we aren't at the start of a frame if(pGlobalData->nextDataPacketNum != 0) { projectedSYT = SubtractFWCycleTimeFromFWCycleTime(projectedSYT, nominalFrameCycleTime); // Add the fraction of the SYT offset between the start of the frame and the // first data packet for the current buffer group. fractionalFrameCycleOffset = ((nominalFrameCycleTime & 0x0FFF) * pGlobalData->nextDataPacketNum) / pGlobalData->numDataPacketsPerFrame; fractionalFrameCycleCount = ((nominalFrameCycleTime & 0x01FFF000) * pGlobalData->nextDataPacketNum) / pGlobalData->numDataPacketsPerFrame; fractionalFrameCycleCount = (fractionalFrameCycleCount & 0x01FFF000) + (((fractionalFrameCycleCount & 0x0FFF) * 3072) / 4096); projectedSYT = AddFWCycleTimeToFWCycleTime (projectedSYT, fractionalFrameCycleOffset); projectedSYT = AddFWCycleTimeToFWCycleTime (projectedSYT, fractionalFrameCycleCount); } // The time drift is the difference between the projected time stamp and SYT. // We must convert the time drift to cycles. cycleDrift = AddFWCycleTimeToFWCycleTime(projectedTimeStamp, kPlaySYTDelay << 12); cycleDrift = SubtractFWCycleTimeFromFWCycleTime(cycleDrift, projectedSYT); //syslog(LOG_INFO, "time drift %x group %p\n", cycleDrift >> 12, pLocalData ); timeDrift = (cycleDrift >> 12) & 0x000F; // Skip an empty packet if we're drifting. // Only consider positive drifting. if ((timeDrift > 0) && (timeDrift < 0x0008)) { ModifyDCLJump(pGlobalData->fStreamVars.pFWLocalIsochPort, pLocalData->pBufferGroupSkipEmptyPacketDCLJump, pLocalData->pBufferGroupSkipEmptyPacketDCLLabel); pGlobalData->activePackets--; pLocalData->skippingEmptyPacket = true; } DVUpdateOutputBuffers( pLocalData ); // Update DCL jumps to call underrun proc after this buffer group. //zzz check errors. ModifyDCLJump (pGlobalData->fStreamVars.pFWLocalIsochPort, pLocalData->pEndOfBufferGroupDCLJump, pGlobalData->pUnderrunDCLLabel); ModifyDCLJump (pGlobalData->fStreamVars.pFWLocalIsochPort, pPrevLocalData->pEndOfBufferGroupDCLJump, pLocalData->pStartOfBufferGroupDCLLabel); pGlobalData->fSharedDCLVars.fDMAPos = pLocalData->fBlockNum; #if TIMING cend = CFAbsoluteTimeGetCurrent(); DVLog(pGlobalData->fStreamVars.fThread, 'isoc', cstart, cend); #endif } static void DVWritePoll(DVGlobalOutPtr globs) { int i, pos; pos = globs->fSharedDCLVars.fDMAPos; for(i=pos; ifLocalDataArray[i].pBufferGroupTimeStampPtr != 0xffffffff) { DVHandleOutput(&globs->fLocalDataArray[i]); *globs->fLocalDataArray[i].pBufferGroupTimeStampPtr = 0xffffffff; } for(i=0; ifLocalDataArray[i].pBufferGroupTimeStampPtr != 0xffffffff) { DVHandleOutput(&globs->fLocalDataArray[i]); *globs->fLocalDataArray[i].pBufferGroupTimeStampPtr = 0xffffffff; } } static void doDVHandleOutputUnderrun( DVGlobalOutPtr pGlobalData ) { IOReturn err; // FIXME syslog(LOG_INFO, "DVHandleOutputUnderrun: 0x%p\n", pGlobalData); DVStream *stream; if ((pGlobalData->pendingDVWriteUnderrunHandler == true) && (pGlobalData->deferredDVWriteFree == true)) { // Free the globalout data struct free(pGlobalData); return; } pGlobalData->pendingDVWriteUnderrunHandler = false; stream = &pGlobalData->fStreamVars; // See if stream still open. If not, we're done! if (stream->fIsochChannelRef == NULL) return; closeStream(&pGlobalData->fStreamVars); FreeDCLCommandPool(pGlobalData); BreakP2PConnectionForWrite(pGlobalData->fStreamVars.pDVDevice,0,pGlobalData->fStreamVars.fIsocChannel); err = buildWriteProgram(pGlobalData); if(err != kIOReturnSuccess) syslog(LOG_INFO, "DVHandleOutputUnderrun: buildWriteProgram returned %x\n", err); err = DVWriteStart(pGlobalData); } static void DVHandleOutputUnderrun( DCLCommandPtr pDCLCommandPtr ) { DVGlobalOutPtr pGlobalData; pGlobalData = (DVGlobalOutPtr)((DCLCallProcPtr)pDCLCommandPtr)->procData; if (pGlobalData->dvWriteStopInProgress == false) { pGlobalData->pendingDVWriteUnderrunHandler = true; DVRequest(pGlobalData->fStreamVars.fThread, doDVHandleOutputUnderrun, pGlobalData, 0); } } static void DVDisposeDCLOutput( DVGlobalOutPtr pOutputData ) { DVLocalOutPtr pLocalData, pNextLocalData; UInt32 bufferGroupNum; // syslog(LOG_INFO, "DVDisposeDCLOutput\n"); if( pOutputData != NULL ) { // Deallocate play buffer group data records. // and update lists associated with them pLocalData = &pOutputData->fLocalDataArray[0]; for (bufferGroupNum = 0; bufferGroupNum < kNumPlayBufferGroups; bufferGroupNum++) { if( pLocalData != NULL ) { pNextLocalData = pLocalData->pNextLocalData; DVDeallocatePlayBufferGroup (pLocalData); pLocalData = pNextLocalData; } } FreeDCLCommandPool(pOutputData); /* don't dispose of the frame buffer here.. wait since its shared DVReleaseFrameIO(); */ if( pOutputData->fStreamVars.fDCLBuffers != NULL) { //free( pOutputData->pTransmitBuffers); //pOutputData->fDCLBufferSize ); vm_deallocate(mach_task_self(), (vm_address_t)pOutputData->fStreamVars.fDCLBuffers, pOutputData->fStreamVars.fDCLBufferSize); } //free( pOutputData); //, sizeof(DVGlobalOut) ); } } static IOReturn allocateBuffers(DVGlobalOutPtr pGlobalData) { UInt32 numDataPacketsPerPage; UInt32 numEmptyPackets; UInt32 pageSize; UInt32 emptySize; // Space used by empty packer headers. UInt32 transmitBuffersSize; IOReturn res; // Setup CIP header static bits, plus packet size based on signal mode UInt8 stype = pGlobalData->fStreamVars.fSignalMode & kAVCSignalModeMask_STYPE; switch (stype) { case kAVCSignalModeMask_SDL: pGlobalData->fHeader0 = 0x003c0000; pGlobalData->fHeader1 = 0x80040000; pGlobalData->fDataPacketSize = kDVSDLPayloadPacketSize; // Data portion, in bytes break; case kAVCSignalModeMask_DVCPro25: pGlobalData->fHeader0 = 0x00780000; pGlobalData->fHeader1 = 0x80780000; pGlobalData->fDataPacketSize = kDVSDPayloadPacketSize; // Data portion, in bytes break; case kAVCSignalModeMask_DVCPro50: pGlobalData->fHeader0 = 0x00784000; pGlobalData->fHeader1 = 0x80740000; pGlobalData->fDataPacketSize = kDVCPro50PayloadPacketSize; // Data portion, in bytes break; case kAVCSignalModeMask_DVCPro100: pGlobalData->fHeader0 = 0x00788000; pGlobalData->fHeader1 = 0x80700000; pGlobalData->fDataPacketSize = kDVCPro50PayloadPacketSize; // Data portion, in bytes break; case kAVCSignalModeMask_HD: pGlobalData->fHeader0 = 0x00F00000; pGlobalData->fHeader1 = 0x80080000; pGlobalData->fDataPacketSize = kDVCPro50PayloadPacketSize; // Data portion, in bytes break; default: // Must be SD video stream pGlobalData->fHeader0 = 0x00780000; pGlobalData->fHeader1 = 0x80000000; pGlobalData->fDataPacketSize = kDVSDPayloadPacketSize; // Data portion, in bytes break; }; pGlobalData->fDataQuadSize = pGlobalData->fDataPacketSize/4; // Data portion, in quads pGlobalData->fAlignQuadSize = (pGlobalData->fDataPacketSize + kDVPacketCIPSize + 15)/16; pGlobalData->fAlignQuadSize *= 4; // Packet size padded out to 16 byte boundary, in quads pGlobalData->fSharedDCLVars.fAlignedPacketSize = 4*pGlobalData->fAlignQuadSize; //Packet size in bytes pGlobalData->fSharedDCLVars.fPacketDataSize = pGlobalData->fDataPacketSize; //////////////////////////////////////////////// // // set timing values for appropriate video system // if( !(pGlobalData->fStreamVars.fSignalMode & kAVCSignalModeMask_50) ) { //syslog(LOG_INFO, "NTSC output\n"); pGlobalData->playFramePeriodNumerator = kNTSCPlayFramePeriodNumerator; pGlobalData->playFramePeriodDenominator = kNTSCPlayFramePeriodDenominator; pGlobalData->playFrameRateNumerator = kNTSCFrameRateNumerator; pGlobalData->playFrameRateDenominator = kNTSCFrameRateDenominator; pGlobalData->numDataPacketsPerFrame = kNTSCNumDataPacketsPerDVFrame; pGlobalData->numDataPacketsPerGroup = kNTSCNumDataPacketsPerGroup; } else { //syslog(LOG_INFO, "PAL output\n"); pGlobalData->fHeader1 |= kPALBit; pGlobalData->playFramePeriodNumerator = kPALPlayFramePeriodNumerator; pGlobalData->playFramePeriodDenominator = kPALPlayFramePeriodDenominator; pGlobalData->playFrameRateNumerator = kPALFrameRateNumerator; pGlobalData->playFrameRateDenominator = kPALFrameRateDenominator; pGlobalData->numDataPacketsPerFrame = kPALNumDataPacketsPerDVFrame; pGlobalData->numDataPacketsPerGroup = kPALNumDataPacketsPerGroup; } // Compute nominal frame period cycle time. pGlobalData->nominalFrameCycleTime = ConvertFractionalSecondsToFWCycleTime (pGlobalData->playFramePeriodNumerator, pGlobalData->playFramePeriodDenominator); pGlobalData->fSharedDCLVars.fNumGroups = kNumPlayBufferGroups; pGlobalData->fSharedDCLVars.fGroupSize = pGlobalData->numDataPacketsPerGroup; pageSize = PAGE_SIZE; numDataPacketsPerPage = pageSize / (pGlobalData->fDataPacketSize + kDVPacketCIPSize + kDVPacketAlignSlop); #if ALT_TIMING numEmptyPackets = getEmptyPacketsPerGroup(pGlobalData, pGlobalData->numDataPacketsPerGroup) * kNumPlayBufferGroups; #else numEmptyPackets = getEmptyPacketsPerGroup(pGlobalData, pGlobalData->numDataPacketsPerGroup * kNumPlayBufferGroups) + kNumPlayBufferGroups/2; #endif transmitBuffersSize = pGlobalData->numDataPacketsPerGroup * kNumPlayBufferGroups * pageSize; transmitBuffersSize /= numDataPacketsPerPage; // add pages for empty packets and time stamps emptySize = numEmptyPackets * (kDVPacketCIPSize + kDVPacketAlignSlop); // Allocate. //syslog(LOG_INFO, "DVWrite: IOMalloc: pGlobalData->pTransmitBuffers size %d, empty %d\n",transmitBuffersSize, emptySize); //pGlobalData->pTransmitBuffers = (UInt8 *)malloc( transmitBuffersSize + emptySize + pageSize ); pGlobalData->fStreamVars.fDCLBufferSize = transmitBuffersSize + emptySize + sizeof(UInt32)*kNumPlayBufferGroups; vm_allocate(mach_task_self(), (vm_address_t *)&pGlobalData->fStreamVars.fDCLBuffers, pGlobalData->fStreamVars.fDCLBufferSize, VM_FLAGS_ANYWHERE); //syslog(LOG_INFO, "DCL buffers at %p\n", pGlobalData->fStreamVars.fDCLBuffers); if( pGlobalData->fStreamVars.fDCLBuffers == NULL ) { res = kIOReturnNoMemory; goto bail; } bzero( pGlobalData->fStreamVars.fDCLBuffers, pGlobalData->fStreamVars.fDCLBufferSize ); pGlobalData->pEmptyTransmitBuffers = pGlobalData->fStreamVars.fDCLBuffers + transmitBuffersSize; pGlobalData->fSharedDCLVars.fTimeStampPtrs = (UInt32 *)(pGlobalData->pEmptyTransmitBuffers + emptySize); return kIOReturnSuccess; bail: DVDisposeDCLOutput( pGlobalData ); return res; } /* The write program is built of kNumPlayBufferGroups blocks of kDCLSendPacketStartOps, kNumDataPacketsPerPlayBufferGroup of the ops in each block send empty DV packets (just the CIP header) so that the timing comes out right. In addition, the callproc (DVHandleOutput) adjusts the blocks to skip an empty packet when the Mac is sending data too slowly. LoopDCL: SetTagSyncBits(tag=1, sync=0) kNumPlayBufferGroups* SendPacketStart (full packet/empty packet) or, for first empty packet in group: Jump (initially to SendEmptyLabel, altered to SkipEmptyLabel when falling behind) SendEmptyLabel: SendPacketStart (empty packet) SkipEmptyLabel: Jump (initially to EndOfGroup, except last group jumps to Underrun) EndOfGroup: TimeStamp UpdateDCLList(TimeStamp) CallProc(DVHandleOutput) UpdateDCLList(All the SendPackets) Jump (LoopDCL) Underrun: CallProc(DVHandleOutputUnderrun) */ static IOReturn buildWriteProgram(DVGlobalOutPtr pGlobalData) { UInt32 numEmptyPacketsInPlayBufferGroup; DCLCommandPtr pDCLCommand; DCLCommandPtr pFirstBufferGroupDCLCommand; DCLLabelPtr pUnderrunDCLLabel, pBufferGroupDCLLabel, pDCLLabel; DCLTransferPacketPtr pDCLTransferPacket; DCLCallProcPtr pDCLCallProc; DCLSetTagSyncBitsPtr pDCLSetTagSyncBits; DCLJumpPtr pDCLJump, pBufferGroupDCLJump; DCLLabelPtr pBufferGroupSkipEmptyPacketDCLLabel; DCLUpdateDCLListPtr pDCLUpdateDCLList; DCLPtrTimeStampPtr pDCLTimeStamp; DVLocalOutPtr pPlayBufferGroupData; UInt32 * pTransmitBuffer; UInt8 * pEmptyTransmitBuffer; volatile UInt32 * pTimeStampPtr; UInt32 bufferGroupNum; UInt32 dataPacketNum; UInt32 numPackets; UInt32 emptyPacketNumerator; UInt32 pageOffset; IOReturn res; UInt32 totalDCLSize; UInt32 totalEmpty, emptySoFar; #if ALT_TIMING totalEmpty = getEmptyPacketsPerGroup(pGlobalData, pGlobalData->numDataPacketsPerGroup) * kNumPlayBufferGroups; #else totalEmpty = getEmptyPacketsPerGroup(pGlobalData, pGlobalData->numDataPacketsPerGroup * kNumPlayBufferGroups) + kNumPlayBufferGroups/2; #endif // syslog(LOG_INFO, "total empty %d\n", totalEmpty); emptySoFar = 0; pTransmitBuffer = (UInt32 *) pGlobalData->fStreamVars.fDCLBuffers; pEmptyTransmitBuffer = pGlobalData->pEmptyTransmitBuffers; pTimeStampPtr = pGlobalData->fSharedDCLVars.fTimeStampPtrs; ///////////////////////////////////////// // Start Up DCL Allocation Engine // Allocate DCL command pool. // // DCLs: // Start Label + SetTagSyncBits + DCLJump + DCLLabel + DCLCallProc // kNumPlayBufferGroups * ( (kNumDataPacketsPerPlayBufferGroup + numEmptyPacketsPerPlayBufferGroup) * DCLTransferPacket + 3 * DCLLabel + 2 * DCLJump + DCLPtrTimeStamp + DCLUpdateDCLList + DCLCallProc + DCLUpdateDCLList) totalDCLSize = 2 * sizeof(DCLLabel) + sizeof(DCLSetTagSyncBits) + sizeof(DCLJump) + sizeof(DCLCallProc) + kNumPlayBufferGroups * (pGlobalData->numDataPacketsPerGroup * sizeof(DCLTransferPacket) + 3*sizeof(DCLLabel) + 2*sizeof(DCLJump) + sizeof(DCLPtrTimeStamp) + 2*sizeof(DCLUpdateDCLList) + sizeof(DCLCallProc) + totalEmpty * sizeof(DCLTransferPacket)); res = AllocateDCLCommandPool(pGlobalData, totalDCLSize); if (res) goto bail; //////////////////////////////////// // Actually Create DCL Program // // Initialize total packet count. pGlobalData->totalPackets = 0; pGlobalData->activePackets = 0; // Set isoch packet tag bits to the way DV likes 'em pDCLSetTagSyncBits = (DCLSetTagSyncBitsPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLSetTagSyncBits)); pDCLCommand = (DCLCommandPtr) pDCLSetTagSyncBits; pDCLSetTagSyncBits->opcode = kDCLSetTagSyncBitsOp; pDCLSetTagSyncBits->tagBits = 1; pDCLSetTagSyncBits->syncBits = 0; // Set the pointer to the start of this DCL program pGlobalData->fStreamVars.pDCLList = pDCLCommand; for (bufferGroupNum = 0; bufferGroupNum < kNumPlayBufferGroups; bufferGroupNum++) { // Allocate a buffer group data record. pPlayBufferGroupData = DVAllocatePlayBufferGroup( pGlobalData, bufferGroupNum); // Initialize for loop. dataPacketNum = 0; numPackets = 0; emptyPacketNumerator = 0; #if ALT_TIMING numEmptyPacketsInPlayBufferGroup = totalEmpty/kNumPlayBufferGroups; #else numEmptyPacketsInPlayBufferGroup = (totalEmpty*(bufferGroupNum+1)+kNumPlayBufferGroups/2)/kNumPlayBufferGroups - emptySoFar; #endif emptySoFar += numEmptyPacketsInPlayBufferGroup; pFirstBufferGroupDCLCommand = NULL; pBufferGroupSkipEmptyPacketDCLLabel = NULL; pGlobalData->fSharedDCLVars.fDataOffset[bufferGroupNum] = (UInt8*)pTransmitBuffer - pGlobalData->fStreamVars.fDCLBuffers; // Create label for start of buffer group. pBufferGroupDCLLabel = (DCLLabelPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLLabel)); pPlayBufferGroupData->pStartOfBufferGroupDCLLabel = pBufferGroupDCLLabel; pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pBufferGroupDCLLabel; pDCLCommand = (DCLCommandPtr) pBufferGroupDCLLabel; pBufferGroupDCLLabel->opcode = kDCLLabelOp; while (dataPacketNum < pGlobalData->numDataPacketsPerGroup) { // Send a packet: CIP header + payload. pDCLTransferPacket = (DCLTransferPacketPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLTransferPacket)); pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLTransferPacket; pDCLCommand = (DCLCommandPtr) pDCLTransferPacket; pDCLTransferPacket->opcode = kDCLSendPacketStartOp; pDCLTransferPacket->size = pGlobalData->fDataPacketSize + kDVPacketCIPSize; // check for buffer crossing page pageOffset = (UInt32) (pTransmitBuffer + pGlobalData->fAlignQuadSize) & 0x0fff; if (pageOffset < (4*pGlobalData->fAlignQuadSize) && pageOffset > 0) { // if it does, increment buffer pointer // and lop off page rollover to start at next page pTransmitBuffer += pGlobalData->fAlignQuadSize; pTransmitBuffer = (UInt32 *)((UInt32)pTransmitBuffer & 0xfffff000); } pDCLTransferPacket->buffer = (UInt8 *) pTransmitBuffer; // increment by multiple of 16 to maintain cache alignment pTransmitBuffer += pGlobalData->fAlignQuadSize; // Save first data packet DCL command. if (pFirstBufferGroupDCLCommand == NULL) pFirstBufferGroupDCLCommand = (DCLCommandPtr) pDCLCommand; dataPacketNum++; numPackets++; emptyPacketNumerator += numEmptyPacketsInPlayBufferGroup; if (emptyPacketNumerator >= pGlobalData->numDataPacketsPerGroup) { // Add skip jump if this is the first empty packet in the buffer group. if (pBufferGroupSkipEmptyPacketDCLLabel == NULL) { pDCLJump = (DCLJumpPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLJump)); pPlayBufferGroupData->pBufferGroupSkipEmptyPacketDCLJump = pDCLJump; pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLJump; pDCLCommand = (DCLCommandPtr) pDCLJump; pDCLJump->opcode = kDCLJumpOp | kFWDCLOpDynamicFlag; pDCLLabel = (DCLLabelPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLLabel)); pPlayBufferGroupData->pBufferGroupDontSkipEmptyPacketDCLLabel = pDCLLabel; pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLLabel; pDCLCommand = (DCLCommandPtr) pDCLLabel; pDCLLabel->opcode = kDCLLabelOp; pDCLJump->pJumpDCLLabel = pDCLLabel; } // Send a packet. // Just CIP header. pDCLTransferPacket = (DCLTransferPacketPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLTransferPacket)); pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLTransferPacket; pDCLCommand = (DCLCommandPtr) pDCLTransferPacket; pDCLTransferPacket->opcode = kDCLSendPacketStartOp; pDCLTransferPacket->buffer = pEmptyTransmitBuffer; pDCLTransferPacket->size = kDVPacketCIPSize; // increment 16 bytes to maintain alignment pEmptyTransmitBuffer += kDVPacketCIPSize+kDVPacketAlignSlop; numPackets++; emptyPacketNumerator -= pGlobalData->numDataPacketsPerGroup; // Add skip jump label if this is the first empty packet in the // buffer group. if (pBufferGroupSkipEmptyPacketDCLLabel == NULL) { // Add skip label. pDCLLabel = (DCLLabelPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLLabel)); pBufferGroupSkipEmptyPacketDCLLabel = pDCLLabel; pPlayBufferGroupData->pBufferGroupSkipEmptyPacketDCLLabel = pBufferGroupSkipEmptyPacketDCLLabel; pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLLabel; pDCLCommand = (DCLCommandPtr) pDCLLabel; pDCLLabel->opcode = kDCLLabelOp; } } } // Save number of packets in this buffer group, DCL update list size, and last // DCL command. pPlayBufferGroupData->numPackets = numPackets; pPlayBufferGroupData->pFirstBufferGroupDCLCommand = pFirstBufferGroupDCLCommand; pPlayBufferGroupData->pLastBufferGroupDCLCommand = (DCLCommandPtr) pDCLCommand; // Create buffer group update list. DVCreatePlayBufferGroupUpdateList( pPlayBufferGroupData ); // Update total packet count. pGlobalData->totalPackets += numPackets; pGlobalData->activePackets += numPackets; // work out if we should start by skipping this empty packet. #if ALT_TIMING { UInt32 nominalProgramCycleTime = 0; UInt32 nominalActivePackets = 0; SInt32 cycleDrift = 0; nominalProgramCycleTime = ConvertFractionalSecondsToFWCycleTime( pGlobalData->playFramePeriodNumerator*(kNumPlayBufferGroups/2), pGlobalData->playFramePeriodDenominator ); nominalActivePackets = ((nominalProgramCycleTime & 0x01FFF000) >> 12); cycleDrift = pGlobalData->activePackets - ((nominalActivePackets*(bufferGroupNum+1)) / kNumPlayBufferGroups); // syslog(LOG_INFO, "Group %d, active %d, nominal %d, cycleDrift %d\n", bufferGroupNum+1, pGlobalData->activePackets-numPackets, ((nominalActivePackets*(bufferGroupNum)) / kNumPlayBufferGroups), cycleDrift); if(cycleDrift > 0) { pGlobalData->activePackets--; pPlayBufferGroupData->pBufferGroupSkipEmptyPacketDCLJump->pJumpDCLLabel = pPlayBufferGroupData->pBufferGroupSkipEmptyPacketDCLLabel; pPlayBufferGroupData->skippingEmptyPacket = true; } else pPlayBufferGroupData->skippingEmptyPacket = false; } #else emptyError = getEmptyPacketsPerGroup(pGlobalData, pGlobalData->numDataPacketsPerGroup*(bufferGroupNum+1)); emptyError = pGlobalData->activePackets - pGlobalData->numDataPacketsPerGroup*(bufferGroupNum+1) - emptyError; //syslog(LOG_INFO, "Group %d, %d empty packets, Current error %d\n", bufferGroupNum, numEmptyPacketsInPlayBufferGroup, emptyError); if(emptyError > 0) { pGlobalData->activePackets--; pPlayBufferGroupData->pBufferGroupSkipEmptyPacketDCLJump->pJumpDCLLabel = pPlayBufferGroupData->pBufferGroupSkipEmptyPacketDCLLabel; pPlayBufferGroupData->skippingEmptyPacket = true; } else pPlayBufferGroupData->skippingEmptyPacket = false; #endif // Get time stamp at end of buffer group. pDCLTimeStamp = (DCLPtrTimeStampPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLPtrTimeStamp)); pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLTimeStamp; pDCLCommand = (DCLCommandPtr) pDCLTimeStamp; pDCLTimeStamp->opcode = kDCLPtrTimeStampOp; *pTimeStampPtr = 0xffffffff; // Init to impossible time stamp pDCLTimeStamp->timeStampPtr = pTimeStampPtr++; pPlayBufferGroupData->pBufferGroupTimeStampPtr = pDCLTimeStamp->timeStampPtr; pPlayBufferGroupData->timeStampUpdateDCLList = (DCLCommandPtr) pDCLTimeStamp; // Create update DCL list to update time stamp. pDCLUpdateDCLList = (DCLUpdateDCLListPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLUpdateDCLList)); pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLUpdateDCLList; pDCLCommand = (DCLCommandPtr) pDCLUpdateDCLList; pDCLUpdateDCLList->opcode = kDCLUpdateDCLListOp; pDCLUpdateDCLList->dclCommandList = &(pPlayBufferGroupData->timeStampUpdateDCLList); pDCLUpdateDCLList->numDCLCommands = 1; #if 0 // TODO: Merge this update DCL list with the timestamp update list, above!!!!! // Create update DCL list to update buffers. pDCLUpdateDCLList = (DCLUpdateDCLListPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLUpdateDCLList)); pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLUpdateDCLList; pDCLCommand = (DCLCommandPtr) pDCLUpdateDCLList; pDCLUpdateDCLList->opcode = kDCLUpdateDCLListOp; pDCLUpdateDCLList->dclCommandList = pPlayBufferGroupData->bufferGroupUpdateDCLList; pDCLUpdateDCLList->numDCLCommands = pPlayBufferGroupData->updateListSize; #endif // Create end of buffer group jump. pBufferGroupDCLJump = (DCLJumpPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLJump)); pPlayBufferGroupData->pEndOfBufferGroupDCLJump = pBufferGroupDCLJump; pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pBufferGroupDCLJump; pDCLCommand = (DCLCommandPtr) pBufferGroupDCLJump; pBufferGroupDCLJump->opcode = kDCLJumpOp | kFWDCLOpDynamicFlag; pBufferGroupDCLJump->pJumpDCLLabel = nil; // For now, this will be updated later! } // Create label for underrun. pUnderrunDCLLabel = (DCLLabelPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLLabel)); pGlobalData->pUnderrunDCLLabel = pUnderrunDCLLabel; pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pUnderrunDCLLabel; pDCLCommand = (DCLCommandPtr) pUnderrunDCLLabel; pUnderrunDCLLabel->opcode = kDCLLabelOp; // Send a garbage packet (just CIP only!). Required for a valid DCL program! pDCLTransferPacket = (DCLTransferPacketPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLTransferPacket)); pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLTransferPacket; pDCLCommand = (DCLCommandPtr) pDCLTransferPacket; pDCLTransferPacket->opcode = kDCLSendPacketStartOp; pDCLTransferPacket->buffer = pGlobalData->pEmptyTransmitBuffers; pDCLTransferPacket->size = kDVPacketCIPSize; // Call underrun proc. // This is the last command. pDCLCallProc = (DCLCallProcPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLCallProc)); pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLCallProc; pDCLCallProc->pNextDCLCommand = NULL; pDCLCallProc->opcode = kDCLCallProcOp; pDCLCallProc->proc = DVHandleOutputUnderrun; pDCLCallProc->procData = (UInt32) pGlobalData; // Fix up jump targets for (bufferGroupNum = 0; bufferGroupNum < kNumPlayBufferGroups; bufferGroupNum++) { pPlayBufferGroupData = &pGlobalData->fLocalDataArray[bufferGroupNum]; if (bufferGroupNum == (kNumPlayBufferGroups-1)) pPlayBufferGroupData->pEndOfBufferGroupDCLJump->pJumpDCLLabel = pGlobalData->pUnderrunDCLLabel; else pPlayBufferGroupData->pEndOfBufferGroupDCLJump->pJumpDCLLabel = pGlobalData->fLocalDataArray[bufferGroupNum+1].pStartOfBufferGroupDCLLabel; } return kIOReturnSuccess; bail: return res; } DVGlobalOutPtr DVAllocWrite(DVDevice *device, DVThread *thread) { DVGlobalOutPtr globs; globs = malloc(sizeof(DVGlobalOut)); if(!globs) return NULL; bzero(globs, sizeof(DVGlobalOut)); initStream(&globs->fStreamVars, device, device->fOutPlug, device->fWriteChan, thread); globs->fUpdateBuffers = 1; return globs; } IOReturn DVWriteSetSignalMode(DVGlobalOutPtr globs, UInt8 mode) { globs->fStreamVars.fSignalMode = mode; switch (mode) { // NTSC SD or DVCPro25 case kAVCSignalModeSD525_60: case kAVCSignalModeDVCPro525_60: globs->fStreamVars.fDVFrameSize = kFrameSize_SD525_60; break; // PAL SD or DVCPro25 case kAVCSignalModeSD625_50: case kAVCSignalModeDVCPro625_50: globs->fStreamVars.fDVFrameSize = kFrameSize_SD625_50; break; // NTSC SDL case kAVCSignalModeSDL525_60: globs->fStreamVars.fDVFrameSize = kFrameSize_SDL525_60; break; // PAL SDL case kAVCSignalModeSDL625_50: globs->fStreamVars.fDVFrameSize = kFrameSize_SDL625_50; break; // NTSC DVCPro50 or HD case kAVCSignalModeDVCPro50_525_60: case kAVCSignalModeHD1125_60: globs->fStreamVars.fDVFrameSize = kFrameSize_DVCPro50_525_60; break; // PAL DVCPro50 or HD case kAVCSignalModeDVCPro50_625_50: case kAVCSignalModeHD1250_50: globs->fStreamVars.fDVFrameSize = kFrameSize_DVCPro50_625_50; break; // NTSC DVCPro100 case kAVCSignalModeDVCPro100_525_60: globs->fStreamVars.fDVFrameSize = kFrameSize_DVCPro100_525_60; break; // PAL DVCPro100 case kAVCSignalModeDVCPro100_625_50: globs->fStreamVars.fDVFrameSize = kFrameSize_DVCPro100_625_50; break; default: globs->fStreamVars.fDVFrameSize = kFrameSize_SD625_50; break; }; return kIOReturnSuccess; } IOReturn DVWriteAllocFrames(DVGlobalOutPtr pGlobalData, UInt32 numFrames, DVFrameVars **frameVars, UInt8 **frames) { IOReturn err; do { err = DVAllocFrames(&pGlobalData->fStreamVars.fFrames, numFrames, pGlobalData->fStreamVars.fDVFrameSize, frameVars, frames); if(err != kIOReturnSuccess) break; err = allocateBuffers(pGlobalData); if(err != kIOReturnSuccess) break; err = buildWriteProgram(pGlobalData); } while (0); return err; } UInt8 * DVWriteGetDCLBuffer(DVGlobalOutPtr pGlobalData, DVSharedVars **varPtr) { // Caller is now responsible for filling up the DCL buffers pGlobalData->fUpdateBuffers = 0; *varPtr = &pGlobalData->fSharedDCLVars; return pGlobalData->fStreamVars.fDCLBuffers; } static IOReturn doDVWriteStart(DVGlobalOutPtr pGlobalData) { IOReturn err; DVLocalOutPtr pPlayBufferGroupData; UInt32 bufferGroupNum; int i; DVThread * dvThread = pGlobalData->fStreamVars.fThread; do { // Set up all of the buffer groups. //syslog(LOG_INFO, "DVWrite: Setup all of the buffer groups\n"); pGlobalData->nextSYT = kPlaySYTDelay<<12; pGlobalData->nextDBC = 0; pGlobalData->nextDataPacketNum = 0; pGlobalData->pImageBuffer = NULL; pGlobalData->fSharedDCLVars.fDMAPos = 0; pGlobalData->pendingDVWriteUnderrunHandler = false; pGlobalData->deferredDVWriteFree = false; pGlobalData->dvWriteStopInProgress = false; pPlayBufferGroupData = &pGlobalData->fLocalDataArray[0]; for (bufferGroupNum = 0; bufferGroupNum < kNumPlayBufferGroups; bufferGroupNum++) { DVUpdateOutputBuffers( pPlayBufferGroupData); pPlayBufferGroupData = pPlayBufferGroupData->pNextLocalData; } err = openStream(&pGlobalData->fStreamVars, true, pGlobalData->fDataPacketSize + kDVPacketCIPSize); for(i=0; ifOutStreams[i] == NULL) { dvThread->fOutStreams[i] = pGlobalData; break; } else if(dvThread->fOutStreams[i] == pGlobalData) { break; // Happens when restarting. } } } while (0); //syslog(LOG_INFO, "doDVWriteStart exit, err %x\n", err); return err; } IOReturn DVWriteStart(DVGlobalOutPtr pGlobalData) { return DVRequest(pGlobalData->fStreamVars.fThread, doDVWriteStart, pGlobalData, 0); } static void doDVWriteStop(DVGlobalOutPtr pGlobalData) { int i; pGlobalData->dvWriteStopInProgress = true; DVThread * dvThread = pGlobalData->fStreamVars.fThread; for(i=0; ifOutStreams[i] == pGlobalData) { dvThread->fOutStreams[i] = NULL; break; } } closeStream(&pGlobalData->fStreamVars); BreakP2PConnectionForWrite(pGlobalData->fStreamVars.pDVDevice,0,pGlobalData->fStreamVars.fIsocChannel); DVDisposeDCLOutput(pGlobalData); pGlobalData->dvWriteStopInProgress = false; } void DVWriteStop(DVGlobalOutPtr pGlobalData) { DVRequest(pGlobalData->fStreamVars.fThread, doDVWriteStop, pGlobalData, 0); } void DVWriteFreeFrames(DVGlobalOutPtr globs) { DVFreeFrames(&globs->fStreamVars.fFrames); } void DVWriteFree(DVGlobalOutPtr globs) { if (globs->pendingDVWriteUnderrunHandler == true) globs->deferredDVWriteFree = true; else free(globs); } DVGlobalInPtr DVAllocRead(DVDevice *device, DVThread *thread) { DVGlobalInPtr globs; globs = malloc(sizeof(DVGlobalIn)); if(!globs) return NULL; bzero(globs, sizeof(DVGlobalIn)); initStream(&globs->fStreamVars, device, kNoPlug, device->fReadChan, thread); // Set the initial read signal mode for standard DV. // This can be overriden later by a call to DVReadSetSignalMode if (device->standard == 0) DVReadSetSignalMode(globs,0x00); // NTSC-DV else DVReadSetSignalMode(globs,0x80); // PAL-DV return globs; } IOReturn DVReadSetSignalMode(DVGlobalInPtr globs, UInt8 mode) { globs->fStreamVars.fSignalMode = mode; switch (mode) { // NTSC SD or DVCPro25 case kAVCSignalModeSD525_60: case kAVCSignalModeDVCPro525_60: globs->fStreamVars.fDVFrameSize = kFrameSize_SD525_60; break; // PAL SD or DVCPro25 case kAVCSignalModeSD625_50: case kAVCSignalModeDVCPro625_50: globs->fStreamVars.fDVFrameSize = kFrameSize_SD625_50; break; // NTSC SDL case kAVCSignalModeSDL525_60: globs->fStreamVars.fDVFrameSize = kFrameSize_SDL525_60; break; // PAL SDL case kAVCSignalModeSDL625_50: globs->fStreamVars.fDVFrameSize = kFrameSize_SDL625_50; break; // NTSC DVCPro50 or HD case kAVCSignalModeDVCPro50_525_60: case kAVCSignalModeHD1125_60: globs->fStreamVars.fDVFrameSize = kFrameSize_DVCPro50_525_60; break; // PAL DVCPro50 or HD case kAVCSignalModeDVCPro50_625_50: case kAVCSignalModeHD1250_50: globs->fStreamVars.fDVFrameSize = kFrameSize_DVCPro50_625_50; break; // NTSC DVCPro100 case kAVCSignalModeDVCPro100_525_60: globs->fStreamVars.fDVFrameSize = kFrameSize_DVCPro100_525_60; break; // PAL DVCPro100 case kAVCSignalModeDVCPro100_625_50: globs->fStreamVars.fDVFrameSize = kFrameSize_DVCPro100_625_50; break; default: globs->fStreamVars.fDVFrameSize = kFrameSize_SD625_50; break; }; return kIOReturnSuccess; } IOReturn DVReadAllocFrames(DVGlobalInPtr globs, UInt32 numFrames, DVFrameVars **frameVars, UInt8 **frames) { return DVAllocFrames(&globs->fStreamVars.fFrames, numFrames, globs->fStreamVars.fDVFrameSize, frameVars, frames); } static void doDVReadHandleInputUnderrun( DVGlobalInPtr pGlobalData ) { UInt32 timeNow, lastFrameTime; DVStream *stream; if ((pGlobalData->pendingDVReadUnderrunHandler == true) && (pGlobalData->deferredDVReadFree == true)) { // Free the globalin data struct free(pGlobalData); return; } pGlobalData->pendingDVReadUnderrunHandler = false; stream = &pGlobalData->fStreamVars; // See if stream still open. If not, we're done! if (stream->fIsochChannelRef == NULL) return; (*stream->pFWDevice)-> GetCycleTime(stream->pFWDevice, &timeNow); syslog(LOG_INFO, "At %8.3f Req time %8.3f, now %8.3f\n", stream->fThread->setTimeoutTime, stream->fThread->requestTimeoutTime, CFAbsoluteTimeGetCurrent()); syslog(LOG_INFO, "DVReadHandleInputUnderrun: 0x%p, last block = %d, status %d, writer %d reader %d timeNow %x\n", pGlobalData, pGlobalData->fState, stream->fFrames.fStatus, stream->fFrames.fWriter, stream->fFrames.fReader, timeNow); DVReadStop(pGlobalData); lastFrameTime = stream->fFrames.fFrameTime[(stream->fFrames.fWriter-1)%stream->fFrames.fNumFrames]; pGlobalData->fSynced = 0; pGlobalData->fRestarted = true; pGlobalData->fLastFrameTime = lastFrameTime; DVReadStart(pGlobalData); } static void DVReadHandleInputUnderrun( DCLCommandPtr pDCLCommandPtr ) { DVGlobalInPtr pGlobalData; pGlobalData = (DVGlobalInPtr)((DCLCallProcPtr)pDCLCommandPtr)->procData; if (pGlobalData->dvReadStopInProgress == false) { pGlobalData->pendingDVReadUnderrunHandler = true; DVRequest(pGlobalData->fStreamVars.fThread, doDVReadHandleInputUnderrun, pGlobalData, 0); } } static void DVStorePackets(DVLocalInPtr pLocalData) { DVGlobalInPtr pGlobalData; DCLCommandPtr pCurrentCmd; DCLTransferPacketPtr pDCLTransferPacket; UInt8 * pPacketBuffer; UInt32 packetHeader, packetSize, packetNum, packetPerFrame; bool vSyncDetected; UInt8 currentSequenceCount; int prevBlock; UInt8 fn; UInt8 stype; #if TIMING CFAbsoluteTime cstart, cend; cstart = CFAbsoluteTimeGetCurrent(); #endif pGlobalData = pLocalData->pGlobalData; //printf("Timestamp %p = %x\n", pLocalData->fTimeStampPtr, *pLocalData->fTimeStampPtr); // Get info from ping pong data. pCurrentCmd = pLocalData->fFirstCmd; // How many packets we talkin'? packetPerFrame = (pGlobalData->fStreamVars.fSignalMode & kAVCSignalModeMask_50) ? kPALNumDataPacketsPerDVFrame : kNTSCNumDataPacketsPerDVFrame; for ( packetNum = 0; packetNum < kNumPacketsPerInputBuffer; packetNum++ ) { // compute size of transfer pDCLTransferPacket = (DCLTransferPacketPtr) pCurrentCmd; pPacketBuffer = (UInt8*)pDCLTransferPacket->buffer; packetHeader = *((UInt32*) pPacketBuffer); pPacketBuffer += 4; // 4 byte 1394 header packetSize = (packetHeader & kFWIsochDataLength) >> kFWIsochDataLengthPhase; #if 1 // Calculate fn fn = ((pPacketBuffer[2] & 0xC0) >> 6); if (fn == 0) fn = 1; else fn = 1 << fn; // Check for corrupt packets, otherwise we may die horribly later in bcopy() if(packetSize < 8) { syslog(LOG_INFO, "DVStorePackets: size %d header 0x%x\n", packetSize, packetHeader); packetSize = 8; } else if(packetSize > 8 && packetSize != (pPacketBuffer[1]*4*fn) + 8) { syslog(LOG_INFO, "DVStorePackets: size %d header 0x%x\n", packetSize, packetHeader); packetSize = 8; } #endif // skip empty packets if( packetSize > 8 ) { // get current data block sequence counter value and increment saved value currentSequenceCount = pPacketBuffer[3]; // Increment lastSequenceCount based on stream type // TODO: This will need to change to support 2x,4x modes on some signal types stype = pGlobalData->fStreamVars.fSignalMode & kAVCSignalModeMask_STYPE; switch (stype) { case kAVCSignalModeMask_DVCPro50: pGlobalData->lastSequenceCount += 2; break; case kAVCSignalModeMask_DVCPro100: pGlobalData->lastSequenceCount += 4; break; case kAVCSignalModeMask_SDL: case kAVCSignalModeMask_DVCPro25: case kAVCSignalModeMask_HD: default: // SD video stream pGlobalData->lastSequenceCount += 1; break; }; // Want size minus CIP header packetSize -= 8; // detect vSync vSyncDetected = ((*(short *)(pPacketBuffer + 8) & 0xE0F8 ) == 0x0000 ); if( vSyncDetected ) { // Calculate when Sync arrived. UInt32 frameEnd = SubtractFWCycleTimeFromFWCycleTime(*pLocalData->fTimeStampPtr, (kNumPacketsPerInputBuffer - packetNum) << 12); UInt32 cip2 = *(UInt32 *)(pPacketBuffer+4); pGlobalData->fStreamVars.fSignalMode = (cip2 >> 16) & 0xff; packetPerFrame = (pGlobalData->fStreamVars.fSignalMode & kAVCSignalModeMask_50) ? kPALNumDataPacketsPerDVFrame : kNTSCNumDataPacketsPerDVFrame; // if we got our frameSync at the right time if( pGlobalData->packetCount == packetPerFrame ) { //printf("Frame received @ %x (w%d r%d)\n", frameEnd, pGlobalData->fStreamVars.fFrames.fWriter, pGlobalData->fStreamVars.fFrames.fReader); // Set amount read DVSetInputFrameSizeAndMode(&pGlobalData->fStreamVars.fFrames, packetPerFrame * packetSize, pGlobalData->fStreamVars.fSignalMode, frameEnd); } else { //printf("Sync detected @ %x\n", frameEnd); if(pGlobalData->fRestarted) { // Calculate how many frames missed. // Fiddly mess from OHCI, have 16 bits from the middle of the cycle register (3 second bits + 13 cyle bits) UInt32 lastFrameTime; SInt32 cycleDiff, secsDiff; UInt32 dropped; lastFrameTime = pGlobalData->fLastFrameTime; cycleDiff = ((frameEnd & 0x01FFF000) - (lastFrameTime & 0x01FFF000)); if(cycleDiff < 0) { cycleDiff += 8000 << 12; frameEnd -= 0x02000000; } secsDiff = (frameEnd & 0x0e000000) - (lastFrameTime & 0x0e000000); if(secsDiff < 0) secsDiff += 0x10000000; secsDiff >>= 25; cycleDiff >>= 12; cycleDiff += secsDiff * 8000; if(pGlobalData->fStreamVars.fSignalMode & kAVCSignalModeMask_50) dropped = (cycleDiff * kPALFrameRateNumerator + (4000*kPALFrameRateDenominator)) / (8000*kPALFrameRateDenominator); else dropped = (cycleDiff * kNTSCFrameRateNumerator + (4000*kNTSCFrameRateDenominator)) / (8000*kNTSCFrameRateDenominator); //printf("At sync, dropped %d frames\n", dropped); pGlobalData->fStreamVars.fFrames.fDroppedFrames += dropped; pGlobalData->fRestarted = false; } } // start a new frame pGlobalData->packetCount = 0; pGlobalData->lastSequenceCount = currentSequenceCount; DVGetNextEmptyInputFrame(&pGlobalData->fStreamVars.fFrames, &(pGlobalData->pImageBuffer), pGlobalData->fStreamVars.fDVFrameSize); //printf("Filling frame %p (w%d r%d)\n", // pGlobalData->pImageBuffer, pGlobalData->fStreamVars.fFrames.fWriter, pGlobalData->fStreamVars.fFrames.fReader); pGlobalData->fSynced = true; } if(pGlobalData->fSynced) { // skip over CIP header pPacketBuffer += 8; // 8 bytes if (currentSequenceCount == pGlobalData->lastSequenceCount && pGlobalData->packetCount < packetPerFrame) { // store the packet bcopy( pPacketBuffer, (void *)((UInt32) pGlobalData->pImageBuffer + (pGlobalData->packetCount * packetSize)), packetSize ); pGlobalData->packetCount++; } else { // IOLog("DVStorePacket: dropped frame: packet out of sequence\n"); // packet out of sequence or too many packets between vSync detection, start new frame //printf("Lost sync: %d-%d; %d-%d\n", // currentSequenceCount, pGlobalData->lastSequenceCount, pGlobalData->packetCount, packetPerFrame); pGlobalData->packetCount = 0; pGlobalData->fSynced = false; } } // Set last count to current count to resynch counts if bad sequence pGlobalData->lastSequenceCount = currentSequenceCount; } // update for next packet pCurrentCmd = pCurrentCmd->pNextDCLCommand; } // This block is ready for reuse, link previous block to this, this one to terminator pGlobalData->fState = pLocalData->fBlockNum; if(pLocalData->fBlockNum == 0) prevBlock = kNumPingPongs-1; else prevBlock = pLocalData->fBlockNum-1; ModifyDCLJump(pGlobalData->fStreamVars.pFWLocalIsochPort, pGlobalData->fLocalDataArray[prevBlock].fStateJmp, pLocalData->fStateLabel); ModifyDCLJump(pGlobalData->fStreamVars.pFWLocalIsochPort, pLocalData->fStateJmp, pGlobalData->fTerminal); #if TIMING cend = CFAbsoluteTimeGetCurrent(); DVLog(pGlobalData->fStreamVars.fThread, 'isoc', cstart, cend); #endif } void DVReadPoll(DVGlobalInPtr globs) { int i, pos; pos = globs->fState; for(i=pos; ifLocalDataArray[i].fTimeStampPtr != 0xffffffff) { DVStorePackets(&globs->fLocalDataArray[i]); *globs->fLocalDataArray[i].fTimeStampPtr = 0xffffffff; } for(i=0; ifLocalDataArray[i].fTimeStampPtr != 0xffffffff) { DVStorePackets(&globs->fLocalDataArray[i]); *globs->fLocalDataArray[i].fTimeStampPtr = 0xffffffff; } } IOReturn DVReadStart(DVGlobalInPtr globs) { DCLCommandPtr opcodes; UInt8 * pingPongBuffer = NULL; UInt8 * pingPongPtr; UInt8 * pDCLCommand; DCLLabelPtr pStartDCLLabel; DCLLabelPtr pBlockDCLLabel; DCLLabelPtr pUnderrunDCLLabel; DCLTransferPacketPtr pDCLTransferPacket; DCLPtrTimeStampPtr pDCLTimeStamp; // DCLCallProcPtr pDCLPingPongProc; DCLCallProcPtr pUnderrunDCLCallProc; DCLJumpPtr pDCLPingPongLoop; int pingPongNum, packetNum; UInt32 updateListSize; UInt32 bufferSize; DCLUpdateDCLListPtr pDCLUpdateDCLList; DCLCommandPtr *updateDCLList, *startUpdateDCLList; DVLocalInPtr pLocalData; IOReturn res; UInt32 * timeStampPtr; int i; UInt32 packetBufferSize; UInt32 alignedDVPacketSize; UInt32 pingPongBufferSize; //syslog(LOG_INFO, "DVReadStart() %p\n", globs); // init variables pingPongBuffer = NULL; globs->fStreamVars.pDCLList = NULL; globs->ppUpdateDCLList = NULL; globs->pImageBuffer = NULL; globs->fStreamVars.fDCLBuffers = NULL; // globs->fStreamVars.fSignalMode = kAVCSignalModeMask_50; // initialize to bigger packets per frame (PAL) globs->packetCount = 0; globs->fState = 0; globs->pendingDVReadUnderrunHandler = false; globs->deferredDVReadFree = false; globs->dvReadStopInProgress = false; switch (globs->fStreamVars.fSignalMode) { case kAVCSignalModeSDL525_60: case kAVCSignalModeSDL625_50: packetBufferSize = 252; alignedDVPacketSize = 512; break; case kAVCSignalModeDVCPro50_525_60: case kAVCSignalModeHD1125_60: case kAVCSignalModeDVCPro50_625_50: case kAVCSignalModeHD1250_50: packetBufferSize = 972; alignedDVPacketSize = 1024; break; case kAVCSignalModeDVCPro100_525_60: case kAVCSignalModeDVCPro100_625_50: packetBufferSize = 1932; alignedDVPacketSize = 2048; break; case kAVCSignalModeSD525_60: case kAVCSignalModeDVCPro525_60: case kAVCSignalModeSD625_50: case kAVCSignalModeDVCPro625_50: default: packetBufferSize = 492; alignedDVPacketSize = 512; break; }; pingPongBufferSize = kNumPingPongs * kNumPacketsPerPingPong * alignedDVPacketSize; // Create ping pong buffer, overrun buffer and time stamp buffer //zzz should allocate in initialization routine. bufferSize = pingPongBufferSize + alignedDVPacketSize + kNumPingPongs * sizeof(UInt32); vm_allocate(mach_task_self(), (vm_address_t *)&pingPongBuffer, bufferSize, VM_FLAGS_ANYWHERE); if (pingPongBuffer == NULL) { // syslog(LOG_INFO, "DVRead::Start : IOMalloc: pingPongBuffer failed\n"); res = kIOReturnNoMemory; goto bail; } timeStampPtr = (UInt32 *)(pingPongBuffer + pingPongBufferSize + alignedDVPacketSize); globs->fStreamVars.fDCLBuffers = pingPongBuffer; globs->fStreamVars.fDCLBufferSize = bufferSize; bzero( pingPongBuffer, bufferSize ); // Get pointer to start of DCL commands and update list. opcodes = (DCLCommandPtr)malloc(kRecordDCLProgramSize); globs->fStreamVars.pDCLList = opcodes; if (opcodes == NULL) { // syslog(LOG_INFO, "DVRead::Start : IOMalloc: opcodes failed\n"); res = kIOReturnNoMemory; goto bail; } bzero( opcodes, kRecordDCLProgramSize ); pDCLCommand = (UInt8 *)opcodes; updateDCLList = (DCLCommandPtr *)malloc(kRecordNumDCLs * sizeof(DCLCommandPtr)); globs->ppUpdateDCLList = updateDCLList; if (updateDCLList == NULL) { // syslog(LOG_INFO, "DVRead::Start : IOMalloc: updateDCLList failed\n"); res = kIOReturnNoMemory; goto bail; } bzero( updateDCLList, kRecordNumDCLs * sizeof(DCLCommandPtr)); // Create label for start of loop. pStartDCLLabel = (DCLLabelPtr) pDCLCommand; pDCLCommand += sizeof (DCLLabel); pStartDCLLabel->pNextDCLCommand = (DCLCommandPtr) pDCLCommand; pStartDCLLabel->opcode = kDCLLabelOp; pingPongPtr = pingPongBuffer; // Create kNumPingPongs ping pong buffer lists of 100 packets each. for (pingPongNum = 0; pingPongNum < kNumPingPongs; pingPongNum++) { // Create the DCL input record record and fill it in. pLocalData = &globs->fLocalDataArray[pingPongNum]; //pLocalData->fFirstCmd = (DCLCommandPtr) pDCLCommand; pLocalData->pGlobalData = globs; pLocalData->fBlockNum = pingPongNum; pLocalData->fTimeStampPtr = timeStampPtr; *timeStampPtr = 0xffffffff; startUpdateDCLList = updateDCLList; updateListSize = 0; // Create block start label pBlockDCLLabel = (DCLLabelPtr) pDCLCommand; pDCLCommand += sizeof (DCLLabel); pBlockDCLLabel->pNextDCLCommand = (DCLCommandPtr) pDCLCommand; pBlockDCLLabel->opcode = kDCLLabelOp; pLocalData->fStateLabel = pBlockDCLLabel; pLocalData->fFirstCmd = (DCLCommandPtr) pDCLCommand; // Create transfer DCL for each packet. for (packetNum = 0; packetNum < kNumPacketsPerPingPong; packetNum++) { // Receive one packet up to packetBufferSize bytes. pDCLTransferPacket = (DCLTransferPacketPtr) pDCLCommand; pDCLCommand += sizeof (DCLTransferPacket); pDCLTransferPacket->pNextDCLCommand = (DCLCommandPtr) pDCLCommand; pDCLTransferPacket->opcode = kDCLReceivePacketStartOp; pDCLTransferPacket->buffer = pingPongPtr; pDCLTransferPacket->size = packetBufferSize; *updateDCLList++ = (DCLCommandPtr) pDCLTransferPacket; updateListSize++; pingPongPtr += alignedDVPacketSize; } // Create timestamp. pDCLTimeStamp = (DCLPtrTimeStampPtr) pDCLCommand; pDCLCommand += sizeof (DCLPtrTimeStamp); pDCLTimeStamp->pNextDCLCommand = (DCLCommandPtr) pDCLCommand; pDCLTimeStamp->opcode = kDCLPtrTimeStampOp; pDCLTimeStamp->timeStampPtr = timeStampPtr++; *updateDCLList++ = (DCLCommandPtr) pDCLTimeStamp; updateListSize++; // Create update DCL list. pDCLUpdateDCLList = (DCLUpdateDCLListPtr) pDCLCommand; pDCLCommand += sizeof (DCLUpdateDCLList); pDCLUpdateDCLList->pNextDCLCommand = (DCLCommandPtr) pDCLCommand; pDCLUpdateDCLList->opcode = kDCLUpdateDCLListOp; pDCLUpdateDCLList->dclCommandList = startUpdateDCLList; pDCLUpdateDCLList->numDCLCommands = updateListSize; // Jump to next block (to terminator for last block) pDCLPingPongLoop = (DCLJumpPtr) pDCLCommand; pDCLCommand += sizeof (DCLJump); pDCLPingPongLoop->pNextDCLCommand = (DCLCommandPtr) pDCLCommand; pDCLPingPongLoop->opcode = kDCLJumpOp | kFWDCLOpDynamicFlag; pDCLPingPongLoop->pJumpDCLLabel = (DCLLabelPtr)pDCLCommand; pLocalData->fStateJmp = pDCLPingPongLoop; } // Create label for underrun. pUnderrunDCLLabel = (DCLLabelPtr) pDCLCommand; //pGlobalData->pUnderrunDCLLabel = pUnderrunDCLLabel; pDCLCommand += sizeof (DCLLabel); pUnderrunDCLLabel->pNextDCLCommand = (DCLCommandPtr) pDCLCommand; pUnderrunDCLLabel->opcode = kDCLLabelOp; globs->fTerminal = pUnderrunDCLLabel; // Create receive command for underrun packet pDCLTransferPacket = (DCLTransferPacketPtr) pDCLCommand; pDCLCommand += sizeof (DCLTransferPacket); pDCLTransferPacket->pNextDCLCommand = (DCLCommandPtr) pDCLCommand; pDCLTransferPacket->opcode = kDCLReceivePacketStartOp; pDCLTransferPacket->buffer = pingPongPtr; pDCLTransferPacket->size = packetBufferSize; // Call underrun proc. pUnderrunDCLCallProc = (DCLCallProcPtr) pDCLCommand; pDCLCommand += sizeof (DCLCallProc); pUnderrunDCLCallProc->pNextDCLCommand = (DCLCommandPtr) pDCLCommand; pUnderrunDCLCallProc->opcode = kDCLCallProcOp; pUnderrunDCLCallProc->proc = DVReadHandleInputUnderrun; pUnderrunDCLCallProc->procData = (UInt32)globs; // Create closed loop at end to flush isoc packets out of FIFO pUnderrunDCLLabel = (DCLLabelPtr) pDCLCommand; pDCLCommand += sizeof (DCLLabel); pUnderrunDCLLabel->pNextDCLCommand = (DCLCommandPtr) pDCLCommand; pUnderrunDCLLabel->opcode = kDCLLabelOp; // Create receive command for underrun packet pDCLTransferPacket = (DCLTransferPacketPtr) pDCLCommand; pDCLCommand += sizeof (DCLTransferPacket); pDCLTransferPacket->pNextDCLCommand = (DCLCommandPtr) pDCLCommand; pDCLTransferPacket->opcode = kDCLReceivePacketStartOp; pDCLTransferPacket->buffer = pingPongPtr; pDCLTransferPacket->size = packetBufferSize; // Loop back to keep dumping packets into the bucket // This is the last command. pDCLPingPongLoop = (DCLJumpPtr) pDCLCommand; pDCLPingPongLoop->pNextDCLCommand = NULL; pDCLPingPongLoop->opcode = kDCLJumpOp; pDCLPingPongLoop->pJumpDCLLabel = pUnderrunDCLLabel; // Initialize isochronous channel. res = openStream(&globs->fStreamVars, false, kDVSDPayloadPacketSize + kDVPacketCIPSize); if(res != kIOReturnSuccess) goto bail; for(i=0; ifStreamVars.fThread->fInStreams[i] == NULL) { globs->fStreamVars.fThread->fInStreams[i] = globs; break; } } //syslog(LOG_INFO, "DVRead::Started()\n"); return kIOReturnSuccess; bail: syslog(LOG_INFO, "DVRead::Start() failed: 0x%x\n", res); //Stop(); return res; } static IOReturn doDVReadStop(DVGlobalInPtr pGlobalData) { int i; pGlobalData->dvReadStopInProgress = true; //syslog(LOG_INFO, "doDVReadStop()0x%x\n", pGlobalData); for(i=0; ifStreamVars.fThread->fInStreams[i] == pGlobalData) { pGlobalData->fStreamVars.fThread->fInStreams[i] = NULL; break; } } #ifdef USE_P2P_CONNECTIONS_FOR_DV_READ BreakP2PConnectionForRead(pGlobalData->fStreamVars.pDVDevice,0,pGlobalData->fStreamVars.fIsocChannel); #endif closeStream(&pGlobalData->fStreamVars); if ( pGlobalData->ppUpdateDCLList) { free( pGlobalData->ppUpdateDCLList); //,kRecordNumDCLs * sizeof(DCLCommandPtr)); pGlobalData->ppUpdateDCLList = NULL; } if ( pGlobalData->fStreamVars.pDCLList) { //bzero(pGlobalData->fStreamVars.pDCLList, kRecordDCLProgramSize); free( pGlobalData->fStreamVars.pDCLList); //, kRecordDCLProgramSize); pGlobalData->fStreamVars.pDCLList = NULL; } if ( pGlobalData->fStreamVars.fDCLBuffers) { vm_deallocate(mach_task_self(), (vm_address_t)pGlobalData->fStreamVars.fDCLBuffers, pGlobalData->fStreamVars.fDCLBufferSize); pGlobalData->fStreamVars.fDCLBuffers = NULL; } pGlobalData->dvReadStopInProgress = false; return kIOReturnSuccess; } void DVReadStop(DVGlobalInPtr pGlobalData) { DVRequest(pGlobalData->fStreamVars.fThread, doDVReadStop, pGlobalData, 0); } void DVReadFreeFrames(DVGlobalInPtr globs) { DVFreeFrames(&globs->fStreamVars.fFrames); } void DVReadFree(DVGlobalInPtr globs) { // Defer freeing of the globalin data struct // if we have a pending input underrun to deal with. if (globs->pendingDVReadUnderrunHandler == true) globs->deferredDVReadFree = true; else free(globs); } void DVLog(DVThread *thread, UInt32 tag, CFAbsoluteTime start, CFAbsoluteTime end) { #if TIMING Log * log; log = &thread->fLog[thread->fLogPos]; log->tag = tag; log->start = start; log->end = end; thread->fLogPos++; if(thread->fLogPos >= kLogSize) thread->fLogPos = 0; #endif } void DVDumpLog(DVThread *thread) { #if TIMING Log * log; UInt32 tag; int i; for(i=thread->fLogPos; ifLog[i]; tag = log->tag; if(tag) { syslog(LOG_INFO, "%d %c%c%c%c %8.3f to %8.3f\n", i, tag>>24, tag>>16, tag>>8, tag, log->start, log->end); } else syslog(LOG_INFO, "%d %x %8.3f to %8.3f\n", i, tag, log->start, log->end); } for(i=0; i< thread->fLogPos; i++) { log = &thread->fLog[i]; tag = log->tag; if(tag) { syslog(LOG_INFO, "%d %c%c%c%c %8.3f to %8.3f\n", i, tag>>24, tag>>16, tag>>8, tag, log->start, log->end); } else syslog(LOG_INFO, "%d %x %8.3f to %8.3f\n", i, tag, log->start, log->end); } #endif }