1/*
2 * Copyright (c) 1998-2002 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include "DVLib.h"
25#include <pthread.h>
26#include <mach/mach_port.h>
27#include <mach/thread_act.h>
28#include <mach/vm_map.h>
29#include <mach/mach_time.h>
30
31#include <syslog.h>	// Debug messages
32
33#include <IOKit/IOMessage.h>
34
35#include <IOKit/firewire/IOFireWireLibIsoch.h>
36#include <IOKit/avc/IOFireWireAVCConsts.h>
37
38#include <IOKit/pwr_mgt/IOPMLib.h>
39
40#include "IsochronousDataHandler.h"
41
42#define kDVRequestID 0
43
44#define ALT_TIMING 1
45
46//#define USE_P2P_CONNECTIONS_FOR_DV_READ 1
47
48#define PAGE_SIZE 4096;
49#define kNoPlug 0xdeadbeef
50
51// Use Channel 62 to send DV, and 63 to receive (because camcorders love to send on 63).
52#define kWriteChannel 62
53#define kReadChannel 61
54
55enum {
56    kNoP2PConnection = 0,
57    kWriteP2PConnection = 1,
58    kReadP2PConnection = 2
59};
60
61// AY_TODO: Move the following to IOFireWireAVCConsts.h
62#define kAVCSignalModeDVCPro50_525_60	0x74
63#define kAVCSignalModeDVCPro50_625_50	0xF4
64#define kAVCSignalModeDVCPro100_525_60	0x70
65#define kAVCSignalModeDVCPro100_625_50	0xF0
66#define kAVCSignalModeMask_DVCPro50		0x74
67#define kAVCSignalModeMask_DVCPro100	0x70
68#define kAVCSignalModeMask_HD			0x08
69
70enum {
71    kDVRunning = 0,
72    kDVStopped = 1,
73    kDVWriteOverrun = 2,
74    kDVReadOverrun = 3,
75    kDVStopping = 4
76};
77
78// Isoc packet header, 2nd quad
79enum {
80    kPALBit									= 0x800000,	// 1 bit
81    kSTypeMask								= 0x7c0000,	// 5 bits
82    kSTypeSD								= 0x00000,
83    kSTypeSDL								= 0x40000,
84    kSTypeHD								= 0x80000
85};
86
87enum {
88
89    // DVRead
90    kNumPacketsPerInputBuffer               = 100,
91    kDVSDPayloadPacketSize                  = 480,
92    kDVSDLPayloadPacketSize                 = 240,
93    kDVHDPayloadPacketSize               	= 960,
94	kDVCPro50PayloadPacketSize				= 960,
95	kDVCPro100PayloadPacketSize				= 1920,
96
97    kNumPingPongs                           = 8,		// Number of DCL blocks in read program
98    kNumPacketsPerPingPong                  = 100,
99    kNumDCLsPerPingPongPacket               = 1,
100    kRecordNumDCLs                          =
101        kNumPingPongs * (kNumPacketsPerPingPong * kNumDCLsPerPingPongPacket+3)+6,
102    kMaxDCLSize                             = 32,
103    kRecordDCLProgramSize                   = kMaxDCLSize * kRecordNumDCLs,
104};
105
106enum {
107    kNTSCFrameRateNumerator                 = 2997,
108    kNTSCFrameRateDenominator               = 100,
109    kNTSCPlayFramePeriodNumerator           = kNTSCFrameRateDenominator,
110    kNTSCPlayFramePeriodDenominator         = kNTSCFrameRateNumerator,
111    kNTSCNumDataPacketsPerDVFrame           = 250,
112    kNTSCNumDataPacketsPerGroup				= 125,
113
114    kPALFrameRateNumerator                  = 25,
115    kPALFrameRateDenominator                = 1,
116    kPALPlayFramePeriodNumerator            = kPALFrameRateDenominator,
117    kPALPlayFramePeriodDenominator          = kPALFrameRateNumerator,
118    kPALNumDataPacketsPerDVFrame            = 300,
119    kPALNumDataPacketsPerGroup				= 150,
120
121    kDVPacketAlignSlop                      = 8,       // add 8 bytes to
122    kDVPacketCIPSize                        = 8,
123    kPlaySYTDelay                           = 3, 		// Sony camcorders send a delay of 2, but VX2000 wants 3 from us...
124};
125
126// Frame Sizes
127enum {
128    kFrameSize_SD525_60						= 120000,
129    kFrameSize_DVCPro525_60					= 120000,
130    kFrameSize_SD625_50						= 144000,
131    kFrameSize_DVCPro625_50					= 144000,
132    kFrameSize_SDL525_60					= 60000,
133    kFrameSize_SDL625_50					= 72000,
134    kFrameSize_DVCPro50_525_60				= 240000,
135    kFrameSize_HD1125_60					= 240000,
136    kFrameSize_DVCPro50_625_50				= 288000,
137    kFrameSize_HD1250_50					= 288000,
138    kFrameSize_DVCPro100_525_60				= 480000,
139    kFrameSize_DVCPro100_625_50				= 576000
140};
141
142typedef struct {
143    UInt32					fRequest;
144    void *					fParams;
145} DVReq;
146
147typedef struct {
148    mach_msg_header_t	msgHdr;
149    DVReq		dvRequest;
150} SendMsg;
151
152typedef struct {
153    mach_msg_header_t	msgHdr;
154    DVReq			dvRequest;
155    mach_msg_trailer_t	trailer;
156} ReceiveMsg;
157
158
159struct DVLocalOutStruct;
160typedef struct DVLocalOutStruct DVLocalOut, *DVLocalOutPtr;
161
162typedef struct _DVStreamStruct {
163    IOFireWireLibDeviceRef pFWDevice;
164    IOFireWireAVCLibProtocolInterface **fAVCProtoInterface;
165    IOFireWireLibLocalIsochPortRef pFWLocalIsochPort;
166    IOFireWireLibRemoteIsochPortRef pFWRemoteIsochPort;
167	IOFireWireLibIsochChannelRef fIsochChannelRef;
168    UInt32			fPlug;			// PCR plug register of local node
169    DVThread *		fThread;
170    DVFrameVars		fFrames;
171    DCLCommandPtr	pDCLList;               // DCLs used for playing.
172    UInt8 *         fDCLBuffers;       // Buffers to use to transfer packet data.
173    UInt32 			fDCLBufferSize;	// Total allocation for output buffers
174    UInt64			fChannelMask;	// Legal channels to use
175    UInt8			fSignalMode;			// signal type
176    UInt8			fIsocChannel;	// Channel to use
177    UInt8			fMaxSpeed;		// Max bus speed for isoc channel
178    DVDevice		*pDVDevice;
179	UInt32			fDVFrameSize;	// Frame size based on current signal mode
180} DVStream;
181
182// structs
183
184struct DVLocalOutStruct
185{
186    DVLocalOutPtr	pNextLocalData;
187    DVLocalOutPtr	pPrevLocalData;
188    DVGlobalOutPtr	pGlobalData;
189    // Pointer to jump command to end of buffer group.
190    DCLJumpPtr		pEndOfBufferGroupDCLJump;
191	// Pointer to label command at start of buffer group.
192    DCLLabelPtr		pStartOfBufferGroupDCLLabel;
193	// Pointer to jump command to use to skip an empty packet.
194    DCLJumpPtr		pBufferGroupSkipEmptyPacketDCLJump;
195    // Label to jump to to skip an empty packet.
196    DCLLabelPtr		pBufferGroupSkipEmptyPacketDCLLabel;
197    // Label to jump to to not skip an empty packet.
198    DCLLabelPtr		pBufferGroupDontSkipEmptyPacketDCLLabel;
199    // Pointer to buffer group's time stamp.
200    UInt32 *		pBufferGroupTimeStampPtr;
201    // Pointer to first DCL command in buffer group.
202    DCLCommandPtr	pFirstBufferGroupDCLCommand;
203    // Pointer to last DCL command in buffer group.
204    DCLCommandPtr	pLastBufferGroupDCLCommand;
205
206    // DCL update list with just the buffer group's time stamp DCL.
207    DCLCommandPtr	timeStampUpdateDCLList;
208    // List of DCL commands that need updating for each run through buffer group.
209    DCLCommandPtr *	bufferGroupUpdateDCLList;
210    // Number of DCL commands in above list.
211    UInt32			updateListSize;
212
213    // Number of packets in this buffer group.
214    UInt32			numPackets;
215    UInt32				fBlockNum;
216    // True if pBufferGroupSkipEmptyPacketDCLJump is set to skip an empty packet.
217    bool			skippingEmptyPacket;
218};
219
220// DVGlobalOut
221
222struct DVGlobalOutStruct {
223    DVStream	fStreamVars;
224    UInt8 *			  fDCLCommandPool;
225    UInt32			  fTotalPool;
226    UInt32			  fAllocatedPool;
227    DVSharedVars      fSharedDCLVars;
228
229    // output structures
230    DVLocalOut		fLocalDataArray[kNumPlayBufferGroups];   // List of buffer group data records.
231    UInt8 *           pEmptyTransmitBuffers;  // Buffer to use for empty transmit packet data.
232    DCLLabelPtr       pUnderrunDCLLabel;      // Pointer to underrun label.
233    UInt32            totalPackets;           // Total number of packets for playing.
234    UInt32            activePackets;          // Total number of active packets for playing.
235    UInt32            nominalFrameCycleTime;  // Nominal cycle time between frames.
236    UInt32            nextSYT;                // Next SYT to use on frame boundary.
237    UInt32            nextDBC;                // Next DBC to use for first packet of next buffer group.
238    UInt32            numDataPacketsPerFrame; // number of data packets per frame (different for NTSC or PAL)
239    UInt32			  numDataPacketsPerGroup; // Number of data packets in each group (different for NTSC or PAL)
240    UInt32            playFramePeriodNumerator;
241    UInt32            playFramePeriodDenominator;
242    UInt32			  playFrameRateNumerator, playFrameRateDenominator;
243    UInt32 fDataPacketSize;		// Data portion, in bytes
244    UInt32 fDataQuadSize;		// Data portion, in quads
245    UInt32 fAlignQuadSize;		// Packet size in quads, padded to 16 byte boundary.
246    UInt32 fHeader0;			// First quad - sourceID, data size, seq. no.
247    UInt32 fHeader1;			// Second quad - format, PAL/NTSC, SType, rsv, SYT
248    // channel buffer structures
249    UInt32            nextDataPacketNum;      // Data packet number for first data packet of next buffer group.
250    UInt32 *          pImageBuffer;           // Buffer to hold image in.
251    bool			fUpdateBuffers;			  // Our job to copy image data?
252    bool pendingDVWriteUnderrunHandler;
253    bool deferredDVWriteFree;
254    bool dvWriteStopInProgress;
255};
256
257typedef struct DVLocalInStruct
258{
259    DVGlobalInPtr		pGlobalData;
260    DCLCommandPtr		fFirstCmd;
261    DCLLabelPtr			fStateLabel;
262    DCLJumpPtr			fStateJmp;
263    UInt32				fBlockNum;
264    UInt32 *			fTimeStampPtr;
265} DVLocalIn, *DVLocalInPtr;
266
267struct DVGlobalInStruct
268{
269    DVStream	fStreamVars;
270    DCLCommandPtr *ppUpdateDCLList;
271    UInt32 packetCount;
272    UInt32 fLastFrameTime;
273    DVLocalIn fLocalDataArray[kNumPingPongs];
274    DCLLabelPtr fTerminal;
275    UInt8 *pImageBuffer;
276    UInt8 lastSequenceCount;
277    UInt8 fState;			// Current DCL block
278    UInt8 fSynced;
279    UInt8 fRestarted;
280    bool pendingDVReadUnderrunHandler;
281    bool deferredDVReadFree;
282    bool dvReadStopInProgress;
283};
284
285static IOReturn buildWriteProgram(DVGlobalOutPtr pGlobalData);
286static IOReturn allocateBuffers(DVGlobalOutPtr pGlobalData);
287static void DVWritePoll(DVGlobalOutPtr globs);
288static void DVReadPoll(DVGlobalInPtr globs);
289static void closeStream(DVStream *stream);
290static void doDVReadHandleInputUnderrun( DVGlobalInPtr pGlobalData );
291static void doDVHandleOutputUnderrun(  DVGlobalOutPtr	pGlobalData );
292
293#include <CoreFoundation/CFRuntime.h>
294
295#if ( MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 ) && defined(CF_USING_COLLECTABLE_MEMORY)
296#define REGISTER_THREADS_WITH_GC 1
297#else // MAC_OS_X_VERSION_MIN_REQUIRED < 1060
298#define REGISTER_THREADS_WITH_GC 0
299#endif // MAC_OS_X_VERSION_MIN_REQUIRED < 1060
300
301#if REGISTER_THREADS_WITH_GC
302#include <dlfcn.h>
303typedef void (*ObjCRegisterThreadWithCollectorPtr)(void);
304#endif REGISTER_THREADS_WITH_GC
305
306UInt32  AddFWCycleTimeToFWCycleTime( UInt32 cycleTime1, UInt32 cycleTime2 )
307{
308    UInt32    secondCount,
309              cycleCount,
310              cycleOffset;
311    UInt32    cycleTime;
312
313    // Add cycle offsets.
314    cycleOffset = (cycleTime1 & 0x0FFF) + (cycleTime2 & 0x0FFF);
315
316    // Add cycle counts.
317    cycleCount = (cycleTime1 & 0x01FFF000) + (cycleTime2 & 0x01FFF000);
318
319    // Add any carry over from cycle offset to cycle count.
320    if (cycleOffset > 3071)
321    {
322        cycleCount += 0x1000;
323        cycleOffset -= 3072;
324    }
325
326    // Add secondCounts.
327    secondCount = (cycleTime1 & 0xFE000000) + (cycleTime2 & 0xFE000000);
328
329    // Add any carry over from cycle count to secondCount.
330    if (cycleCount > (7999 << 12))
331    {
332        secondCount += 0x02000000;
333        cycleCount -= (8000 << 12);
334    }
335
336    // Put everything together into cycle time.
337    cycleTime = secondCount | cycleCount | cycleOffset;
338
339    return (cycleTime);
340}
341
342UInt32 SubtractFWCycleTimeFromFWCycleTime( UInt32 cycleTime1, UInt32 cycleTime2)
343{
344    SInt32 secondCount,
345           cycleCount,
346           cycleOffset;
347    UInt32 cycleTime;
348
349    // Subtract cycle offsets.
350    cycleOffset = (cycleTime1 & 0x0FFF) - (cycleTime2 & 0x0FFF);
351
352    // Subtract cycle counts.
353    cycleCount = (cycleTime1 & 0x01FFF000) - (cycleTime2 & 0x01FFF000);
354
355    // Subtract any borrow over from cycle offset to cycle count.
356
357    if (cycleOffset < 0)
358    {
359        cycleCount -= 0x1000;
360        cycleOffset += 3072;
361    }
362
363    // Subtract secondCounts.
364    secondCount = (cycleTime1 & 0xFE000000) - (cycleTime2 & 0xFE000000);
365
366    // Subtract any borrow over from cycle count to secondCount.
367    if (cycleCount < 0)
368    {
369        secondCount -= 0x02000000;
370        cycleCount += (8000 << 12);
371    }
372
373    // Put everything together into cycle time.
374    cycleTime = secondCount | cycleCount | cycleOffset;
375
376    return (cycleTime);
377}
378
379static UInt32 ConvertFractionalSecondsToFWCycleTime( UInt32 secondsNumerator, UInt32 secondsDenominator )
380{
381    // float  fSecondCount;
382    // float  fCycleCount;
383    // float  fCycleOffset;
384    // UInt32 iSecondsCount;
385    // UInt32 iCycleCount;
386    // UInt32 iCycleOffset;
387    // UInt32 secondsCycleTime;
388
389    UInt32 iSecondsCount2;
390    UInt32 iCycleCount2;
391    UInt32 iCycleOffset2;
392    UInt32 mSecondCount;
393    UInt32 mCycleCount;
394    UInt32 secondsCycleTime2;
395
396    // Convert fractional seconds into floating point and compute seconds count.
397    // fSecondCount = ((float) secondsNumerator) / ((float) secondsDenominator);
398    // iSecondsCount = (UInt32) fSecondCount;
399    iSecondsCount2 = secondsNumerator / secondsDenominator;
400    mSecondCount = secondsNumerator % secondsDenominator;
401
402    // Subtract whole seconds out of fSecondCount and convert to cycle count.
403    // fCycleCount = (fSecondCount - ((float) iSecondsCount)) * 8000.0;
404    // iCycleCount = (UInt32) fCycleCount;
405    iCycleCount2 = (mSecondCount * 8000) / secondsDenominator;
406    mCycleCount = (mSecondCount * 8000) % secondsDenominator;
407
408    // Subtract whole cycles out of fCycleCount and convert to cycle offset.
409    // fCycleOffset = (fCycleCount - ((float) iCycleCount)) * 3072.0;
410    // iCycleOffset = (UInt32) fCycleOffset;
411    iCycleOffset2 = (mCycleCount * 3072) / secondsDenominator;
412
413    // Convert to cycle timer format.
414    // secondsCycleTime = (iSecondsCount << 25) | (iCycleCount << 12) | iCycleOffset;
415    secondsCycleTime2 = (iSecondsCount2 << 25) | (iCycleCount2 << 12) | iCycleOffset2;
416
417    return (secondsCycleTime2);
418}
419
420static IOReturn writePlug(IOFireWireAVCLibProtocolInterface **interface, UInt32 plug, UInt32 val)
421{
422    return (*interface)->updateOutputPlug(interface, plug,
423        (*interface)->readOutputPlug(interface, plug), val);
424}
425
426static void handlePCRLock(void *refcon, UInt32 generation, UInt16 nodeID, UInt32 plug,
427                                                                    UInt32 oldVal, UInt32 newVal)
428{
429    //syslog(LOG_INFO, "Plug %d written to, old val 0x%x new val 0x%x\n", plug, oldVal, newVal);
430}
431
432static IOReturn writeDeviceOutputMCR(IOFireWireLibDeviceRef interface, UInt32 mask, UInt32 val)
433{
434    UInt32 oldVal, newVal;
435	UInt32 oldValHost, newValHost;
436    IOReturn err;
437    FWAddress addr;
438    io_object_t obj;
439
440    addr.nodeID = 0;
441    addr.addressHi = 0xffff;
442    addr.addressLo = 0xf0000900;
443    obj = (*interface)->GetDevice(interface);
444    err = (*interface)->ReadQuadlet(interface, obj, &addr, &oldVal, false, 0);
445	oldValHost = EndianU32_BtoN( oldVal );
446
447    if(err == kIOReturnSuccess) {
448        if( (oldValHost & mask) != val) {
449            newValHost = (oldValHost & ~mask) | val;
450			newVal = EndianU32_NtoB( newValHost );
451            err = (*interface)->CompareSwap(interface, obj, &addr, oldVal, newVal, false, 0);
452        }
453    }
454    return err;
455}
456
457static IOReturn MakeP2PConnectionForWrite(DVDevice *pDVDevice,UInt32 plug, UInt32 chan)
458{
459    IOReturn err;
460
461#ifdef kIDH_Verbose_Debug_Logging
462	syslog(LOG_INFO, "DVLib: MakeP2PConnectionForWrite begin\n");
463#endif
464
465	err = (*pDVDevice->fAVCInterface)->makeP2PInputConnection(pDVDevice->fAVCInterface, plug, chan);
466    if (err == kIOReturnSuccess)
467    {
468        pDVDevice->p2pConnected = kWriteP2PConnection;
469        pDVDevice->p2pPlug = plug;
470        pDVDevice->p2pChan = chan;
471    }
472
473#ifdef kIDH_Verbose_Debug_Logging
474	syslog(LOG_INFO, "DVLib: MakeP2PConnectionForWrite end\n");
475#endif
476
477	return err;
478}
479
480static IOReturn BreakP2PConnectionForWrite(DVDevice *pDVDevice,UInt32 plug, UInt32 chan)
481{
482    IOReturn err;
483
484#ifdef kIDH_Verbose_Debug_Logging
485	syslog(LOG_INFO, "DVLib: BreakP2PConnectionForWrite begin\n");
486#endif
487
488	err = (*pDVDevice->fAVCInterface)->breakP2PInputConnection(pDVDevice->fAVCInterface, plug);
489
490	// Always clear the connected flag, even if there was an error.
491    pDVDevice->p2pConnected = kNoP2PConnection;
492
493#ifdef kIDH_Verbose_Debug_Logging
494	syslog(LOG_INFO, "DVLib: BreakP2PConnectionForWrite end\n");
495#endif
496
497	return err;
498}
499
500static IOReturn MakeP2PConnectionForRead(DVDevice *pDVDevice,UInt32 plug, UInt32 chan)
501{
502    IOReturn err;
503
504#ifdef kIDH_Verbose_Debug_Logging
505	syslog(LOG_INFO, "DVLib: MakeP2PConnectionForRead begin\n");
506#endif
507
508	err = (*pDVDevice->fAVCInterface)->makeP2POutputConnection(pDVDevice->fAVCInterface, plug,chan,kFWSpeedInvalid);
509    if (err == kIOReturnSuccess)
510    {
511        pDVDevice->p2pConnected = kReadP2PConnection;
512        pDVDevice->p2pPlug = plug;
513        pDVDevice->p2pChan = chan;
514    }
515
516#ifdef kIDH_Verbose_Debug_Logging
517	syslog(LOG_INFO, "DVLib: MakeP2PConnectionForRead end\n");
518#endif
519
520	return err;
521}
522
523static IOReturn BreakP2PConnectionForRead(DVDevice *pDVDevice,UInt32 plug, UInt32 chan)
524{
525    IOReturn err;
526
527#ifdef kIDH_Verbose_Debug_Logging
528	syslog(LOG_INFO, "DVLib: BreakP2PConnectionForRead begin\n");
529#endif
530
531	err = (*pDVDevice->fAVCInterface)->breakP2POutputConnection(pDVDevice->fAVCInterface, plug);
532
533	// Always clear the connected flag, even if there was an error.
534    pDVDevice->p2pConnected = kNoP2PConnection;
535
536#ifdef kIDH_Verbose_Debug_Logging
537	syslog(LOG_INFO, "DVLib: BreakP2PConnectionForRead end\n");
538#endif
539
540    return err;
541}
542
543void AVCUnitMessageCallback(void * refCon, UInt32 type, void * arg )
544{
545    DVDevice *pDVDevice = (DVDevice*) refCon;
546
547#ifdef kIDH_Verbose_Debug_Logging
548	syslog(LOG_INFO, "DVLib: AVCUnitMessageCallback begin, type = 0x%08X\n",type);
549#endif
550
551    // If this is a bus-reset notification, see if we have a p2p connection.
552    // If so, restore the P2P connection, do on real time thread for safety.
553    // Done by kernel now.
554    // Callback the client's message notification handler
555    if (pDVDevice->fThread->fDeviceMessage != NULL)
556        pDVDevice->fThread->fDeviceMessage((void*)pDVDevice->deviceIndex,type,arg);
557
558#ifdef kIDH_Verbose_Debug_Logging
559	syslog(LOG_INFO, "DVLib: AVCUnitMessageCallback end\n");
560#endif
561
562    return;
563}
564
565static IOReturn getSignalMode(IOFireWireAVCLibUnitInterface **avc, UInt8 *mode)
566{
567    UInt32 size;
568    UInt8 cmd[4],response[4];
569    IOReturn res;
570
571#ifdef kIDH_Verbose_Debug_Logging
572	syslog(LOG_INFO, "DVLib: getSignalMode begin\n");
573#endif
574
575    // build query Output Signal Mode command
576    cmd[0] = kAVCStatusInquiryCommand;
577    cmd[1] = IOAVCAddress(kAVCTapeRecorder, 0);
578    cmd[2] = kAVCOutputSignalModeOpcode;
579    cmd[3] = kAVCSignalModeDummyOperand;
580    size = 4;
581    res = (*avc)->AVCCommand(avc, cmd, 4, response, &size);
582    if(res == kIOReturnSuccess) {
583        *mode =  response[3];
584    }
585
586#ifdef kIDH_Verbose_Debug_Logging
587	syslog(LOG_INFO, "DVLib: getSignalMode end\n");
588#endif
589
590	return res;
591}
592
593static bool isDVCPro(IOFireWireAVCLibUnitInterface **avc, UInt8 *pMode)
594{
595    UInt32 size;
596    UInt8 cmd[10],response[10];
597    IOReturn res;
598
599#ifdef kIDH_Verbose_Debug_Logging
600	syslog(LOG_INFO, "DVLib: isDVCPro begin\n");
601#endif
602
603    // build query vender-dependent command (is DVCPro?).
604    cmd[0] = kAVCStatusInquiryCommand;
605    cmd[1] = kAVCUnitAddress;
606    cmd[2] = kAVCVendorDependentOpcode;
607    cmd[3] = 0;
608    cmd[4] = 0x80;
609    cmd[5] = 0x45;
610    cmd[6] = 0x82;
611    cmd[7] = 0x48;
612    cmd[8] = 0xff;
613    cmd[9] = 0xff;
614    size = 10;
615    res = (*avc)->AVCCommand(avc, cmd, 10, response, &size);
616
617	// If it is DVCPro50, see if its 25 or 50
618	if ((res == kIOReturnSuccess) && (response[0] == kAVCImplementedStatus))
619	{
620		cmd[0] = kAVCStatusInquiryCommand;
621		cmd[1] = kAVCUnitAddress;
622		cmd[2] = kAVCOutputPlugSignalFormatOpcode;
623		cmd[3] = 0;
624		cmd[4] = 0xFF;
625		cmd[5] = 0xFF;
626		cmd[6] = 0xFF;
627		cmd[7] = 0xFF;
628		size = 8;
629
630		res = (*avc)->AVCCommand(avc, cmd, 8, response, &size);
631
632		if (res == kIOReturnSuccess && response[0] == kAVCImplementedStatus)
633			*pMode = response[5];
634		else
635			*pMode = 0x00;
636
637#ifdef kIDH_Verbose_Debug_Logging
638		syslog(LOG_INFO, "DVLib: isDVCPro end:true\n");
639#endif
640
641		return true;
642	}
643	else
644	{
645
646#ifdef kIDH_Verbose_Debug_Logging
647		syslog(LOG_INFO, "DVLib: isDVCPro end:false\n");
648#endif
649		return false;
650	}
651}
652
653static bool isSDL(IOFireWireAVCLibUnitInterface **avc, UInt8 signalMode)
654{
655    // Try setting input mode to SDL
656    IOReturn res;
657    bool hasSDL;
658
659    UInt32 size;
660    UInt8 cmd[4],response[4];
661
662#ifdef kIDH_Verbose_Debug_Logging
663	syslog(LOG_INFO, "DVLib: isSDL begin\n");
664#endif
665
666    cmd[0] = kAVCControlCommand;
667    cmd[1] = IOAVCAddress(kAVCTapeRecorder, 0);
668    cmd[2] = kAVCInputSignalModeOpcode;
669    cmd[3] = (signalMode & ~kAVCSignalModeMask_STYPE) | kAVCSignalModeMask_SDL;
670    size = 4;
671    res = (*avc)->AVCCommand(avc, cmd, 4, response, &size);
672    if(res != kIOReturnSuccess || response[0] != kAVCAcceptedStatus)
673	{
674#ifdef kIDH_Verbose_Debug_Logging
675		syslog(LOG_INFO, "DVLib: isSDL end:false\n");
676#endif
677        return false;	// Failed to set to SDL
678	}
679
680    cmd[0] = kAVCStatusInquiryCommand;
681    cmd[1] = IOAVCAddress(kAVCTapeRecorder, 0);
682    cmd[2] = kAVCInputSignalModeOpcode;
683    cmd[3] = kAVCSignalModeDummyOperand;
684    size = 4;
685    res = (*avc)->AVCCommand(avc, cmd, 4, response, &size);
686    hasSDL = (response[3] & kAVCSignalModeMask_STYPE) == kAVCSignalModeMask_SDL;
687
688    // Set back to original value
689    cmd[0] = kAVCControlCommand;
690    cmd[1] = IOAVCAddress(kAVCTapeRecorder, 0);
691    cmd[2] = kAVCInputSignalModeOpcode;
692    cmd[3] = signalMode;
693    size = 4;
694    res = (*avc)->AVCCommand(avc, cmd, 4, response, &size);
695
696#ifdef kIDH_Verbose_Debug_Logging
697	syslog(LOG_INFO, "DVLib: isSDL end:%s\n",(hasSDL==true ? "true" : "false"));
698#endif
699
700    return hasSDL;
701}
702
703static bool isMPEG(IOFireWireAVCLibUnitInterface **avc)
704{
705    UInt32 size;
706    UInt8 cmd[8],response[8];
707    IOReturn res;
708
709	cmd[0] = kAVCStatusInquiryCommand;
710	cmd[1] = kAVCUnitAddress;
711	cmd[2] = kAVCOutputPlugSignalFormatOpcode;
712	cmd[3] = 0;
713	cmd[4] = 0xFF;
714	cmd[5] = 0xFF;
715	cmd[6] = 0xFF;
716	cmd[7] = 0xFF;
717	size = 8;
718
719	res = (*avc)->AVCCommand(avc, cmd, 8, response, &size);
720
721	if ((res == kIOReturnSuccess) &&
722	 (response[0] == kAVCImplementedStatus) &&
723	 (response[4] == 0xA0))
724		return true;
725	else
726		return false;
727}
728
729static void deviceArrived(void *refcon, io_iterator_t iterator )
730{
731    io_object_t obj;
732    DVThread * dvThread = (DVThread *)refcon;
733	UInt8 dvcProMode;
734
735#ifdef kIDH_Verbose_Debug_Logging
736	syslog(LOG_INFO, "DVLib: deviceArrived begin\n");
737#endif
738
739    //syslog(LOG_INFO,"deviceArrived(0x%x, 0x%x)\n", refcon, iterator);
740    while(obj = IOIteratorNext(iterator)) {
741        CFMutableDictionaryRef properties;
742        CFNumberRef dataDesc;
743        CFStringRef strDesc;
744        kern_return_t err;
745        UInt64 GUID;
746        int refound = 0;
747        int device;
748        DVDevice *dev = NULL;
749
750        //syslog(LOG_INFO, "object 0x%x arrived!\n", obj);
751        err = IORegistryEntryCreateCFProperties(obj, &properties, kCFAllocatorDefault, kNilOptions);
752
753        dataDesc = (CFNumberRef)CFDictionaryGetValue(properties, CFSTR("GUID"));
754        CFNumberGetValue(dataDesc, kCFNumberSInt64Type, &GUID);
755        for(device=0; device<dvThread->fNumDevices; device++) {
756            if(GUID == dvThread->fDevices[device].fGUID) {
757                refound = 1;
758                dev = &dvThread->fDevices[device];
759                break;
760            }
761        }
762        if(!refound) {
763            CFBooleanRef hasFCP;
764            device = dvThread->fNumDevices;
765            dvThread->fNumDevices++;
766            dev = &dvThread->fDevices[device];
767            strDesc = (CFStringRef)CFDictionaryGetValue(properties, CFSTR("FireWire Product Name"));
768            if(strDesc) {
769                dev->fName[0] = 0;
770                CFStringGetCString(strDesc, dev->fName, sizeof(dev->fName), kCFStringEncodingMacRoman);
771            }
772            hasFCP = (CFBooleanRef)CFDictionaryGetValue(properties, CFSTR("supportsFCP"));
773            dev->fSupportsFCP = true;
774            if(hasFCP)
775                dev->fSupportsFCP = CFBooleanGetValue(hasFCP);
776
777            dev->fGUID = GUID;
778            dev->fMaxSpeed = kFWSpeed100MBit;
779            dev->fWriteChan = kWriteChannel;
780            dev->fReadChan = kReadChannel;
781       }
782        CFRelease(properties);
783
784        dev->fObject = obj;
785        dev->fThread = dvThread;
786
787        // Request notification of messages via AVC user client
788        err = openAVCUnit(dev->fObject, &dev->fAVCInterface, dvThread);
789        if(err == kIOReturnSuccess)
790		{
791            UInt8 mode, stype;
792
793			// Exclude DVCProHD and MPEG devices from the IDH device list!
794			if (dev->fSupportsFCP)
795			{
796				if(isDVCPro(dev->fAVCInterface,&dvcProMode))
797				{
798					if ((dvcProMode == kAVCSignalModeDVCPro100_525_60) || (dvcProMode == kAVCSignalModeDVCPro100_625_50))
799					{
800						// Terminate this device
801						DVDeviceTerminate(dev);
802
803						// Remove this device from the device list
804						dvThread->fNumDevices--;
805
806						continue;	// continue to next device!
807					}
808				}
809				else if (isMPEG(dev->fAVCInterface))
810				{
811						// Terminate this device
812						DVDeviceTerminate(dev);
813
814						// Remove this device from the device list
815						dvThread->fNumDevices--;
816
817						continue;	// continue to next device!
818				}
819			}
820
821            dev->deviceIndex = device+1;
822            (*dev->fAVCInterface)->setMessageCallback(dev->fAVCInterface, (void *) dev, AVCUnitMessageCallback);
823
824			// Determine mode(s) supported
825            if(dev->fSupportsFCP)
826			{
827                err = getSignalMode(dev->fAVCInterface, &mode);
828                if(err == kIOReturnSuccess)
829				{
830                    if(mode & kAVCSignalModeMask_50)
831                        dev->standard = palIn;
832					else
833						dev->standard = ntscIn;
834
835					// See if DVCPro25 type device
836					stype = mode & kAVCSignalModeMask_STYPE;
837					if(stype == kAVCSignalModeMask_DVCPro25)
838					{
839						dev->fDVFormats |= 1 << kIDHDVCPro_25;
840					}
841					else if(stype == kAVCSignalModeMask_DVCPro50)
842					{
843						dev->fDVFormats |= 1 << kIDHDVCPro_50;
844						dev->fMaxSpeed = kFWSpeed400MBit;	// Default to 400 for DVCPro-50
845					}
846					else
847					{
848						// Ask device via vender-dependent command if it's a DVCPro device.
849						if(isDVCPro(dev->fAVCInterface,&dvcProMode))
850						{
851							if((dvcProMode & kAVCSignalModeMask_STYPE) == kAVCSignalModeMask_DVCPro50)
852							{
853								dev->fDVFormats |= 1 << kIDHDVCPro_50;
854								dev->fMaxSpeed = kFWSpeed400MBit;	// Default to 400 for DVCPro-50
855							}
856							else
857								dev->fDVFormats |= 1 << kIDHDVCPro_25;
858						}
859					}
860
861					// See if SDL type device
862					if(stype == kAVCSignalModeMask_SDL)
863						dev->fDVFormats |= 1 << kIDHDV_SDL;
864					else
865					{
866						// Ask camera if it's SDL.
867						if(isSDL(dev->fAVCInterface, mode))
868							dev->fDVFormats |= 1 << kIDHDV_SDL;
869					}
870				}
871				else
872				{
873					// Failed the signal mode command. Assume standard NTSC DV
874					dev->fDVFormats = 1 << kIDHDV_SD;	// Standard DV
875					dev->standard = ntscIn;
876				}
877			}
878			else
879			{
880				// Assume NTSC, standard DV if device doesn't support AVC.
881				dev->fDVFormats = 1 << kIDHDV_SD;	// Standard DV
882				dev->standard = ntscIn; 			// device standard - NTSC/PAL
883			}
884
885            // Notify client
886            (dvThread->fAddedFunc)(dvThread->fAddedRefCon, dev, device+1, refound);
887		}
888    }
889
890#ifdef kIDH_Verbose_Debug_Logging
891	syslog(LOG_INFO, "DVLib: deviceArrived end\n");
892#endif
893
894}
895
896static OSStatus DVthreadExit(DVThread *dvThread, UInt32 params)
897{
898
899#ifdef kIDH_Verbose_Debug_Logging
900	syslog(LOG_INFO, "DVLib: DVthreadExit begin\n");
901#endif
902
903    if(dvThread->fNotifySource)
904        CFRunLoopSourceInvalidate(dvThread->fNotifySource);
905
906    if(dvThread->fPowerNotifySource)
907        CFRunLoopSourceInvalidate(dvThread->fPowerNotifySource);
908
909	// we have to do this because CF sometimes adds it's own source to our run loop (?!)
910	// which we don't (can't?) invalidate
911	// this makes sure our thread will really exit..
912	CFRunLoopStop(dvThread->fWorkLoop) ;
913    dvThread->fTimerFunc = NULL;
914
915#ifdef kIDH_Verbose_Debug_Logging
916	syslog(LOG_INFO, "DVLib: DVthreadExit end\n");
917#endif
918
919    return noErr;
920}
921
922static void *DVRTThreadStart(DVThread *dvThread)
923{
924
925#if REGISTER_THREADS_WITH_GC
926	if (CF_USING_COLLECTABLE_MEMORY)
927	{
928		void *dlhandle = dlopen("/usr/lib/libobjc.dylib", RTLD_LAZY | RTLD_LOCAL);
929		if( dlhandle != NULL ) {
930			ObjCRegisterThreadWithCollectorPtr objcRegisterThreadWithCollector = dlsym( dlhandle, "objc_registerThreadWithCollector" );
931			if( objcRegisterThreadWithCollector != NULL )
932				objcRegisterThreadWithCollector();
933			else syslog(LOG_INFO, "dlsym(objc_registerThreadWithCollector) failed");
934			dlclose( dlhandle );
935		}
936		else syslog(LOG_INFO, "dlopen(/usr/lib/libobjc.dylib) failed");
937	}
938#endif	// REGISTER_THREADS_WITH_GC
939
940    ReceiveMsg msg;
941    kern_return_t err;
942    int delay;
943    int run = true;
944    int i;
945
946#ifdef kIDH_Verbose_Debug_Logging
947	syslog(LOG_INFO, "DVLib: DVRTThreadStart begin\n");
948#endif
949
950    deviceArrived(dvThread, dvThread->fMatchEnumer);
951    // signal that we're about to start the mach loop
952    DVSignalSync(&dvThread->fRequestSyncer, &dvThread->fSyncRequest, 1);
953
954    delay = 12;	// DCL block size
955    while(run) {
956        int nextTick;
957#if TIMING
958        CFAbsoluteTime start;
959        start = CFAbsoluteTimeGetCurrent();
960#endif
961        err = mach_msg(&msg.msgHdr, MACH_RCV_MSG | MACH_RCV_TIMEOUT,
962        //err = mach_msg(&msg.msgHdr, MACH_RCV_MSG | MACH_RCV_TIMEOUT | MACH_RCV_LARGE,
963                       0, sizeof(msg), dvThread->fRequestMachPort, delay, MACH_PORT_NULL);
964
965#if TIMING
966        DVLog(dvThread, 'mmsg', start, CFAbsoluteTimeGetCurrent());
967#endif
968        if(err == MACH_MSG_SUCCESS) {
969            switch (msg.msgHdr.msgh_id) {
970            case kDVRequestID:
971                dvThread->fRequestResult = (dvThread->fRequestFunc)(dvThread->fRequestArg, dvThread->fRequestParam);
972				if(dvThread->fRequestFunc == DVthreadExit)
973                    run = false;
974                DVSignalSync(&dvThread->fRequestSyncer, &dvThread->fSyncRequest, (UInt32)dvThread->fRequestFunc);
975            }
976        }
977        for(i=0; i<kDVMaxStreamsActive; i++) {
978            if(dvThread->fInStreams[i])
979                DVReadPoll(dvThread->fInStreams[i]);
980            if(dvThread->fOutStreams[i])
981                DVWritePoll(dvThread->fOutStreams[i]);
982        }
983        if(dvThread->fTimerFunc) {
984            dvThread->fTimerFunc(NULL, dvThread->fTimerRefCon);
985            delay = 12;	// DCL block size in milliseconds
986            nextTick = (int)((dvThread->requestTimeoutTime-CFAbsoluteTimeGetCurrent())*1000.0);
987            if(nextTick <= 0)
988                nextTick = 1;
989            if(nextTick < delay)
990                delay = nextTick;
991        }
992
993    }
994
995#ifdef kIDH_Verbose_Debug_Logging
996	syslog(LOG_INFO, "DVLib: DVRTThreadStart end\n");
997#endif
998
999    return NULL;
1000}
1001
1002static void *DVRLThreadStart(DVThread *thread)
1003{
1004#if REGISTER_THREADS_WITH_GC
1005	if (CF_USING_COLLECTABLE_MEMORY)
1006	{
1007		void *dlhandle = dlopen("/usr/lib/libobjc.dylib", RTLD_LAZY | RTLD_LOCAL);
1008		if( dlhandle != NULL ) {
1009			ObjCRegisterThreadWithCollectorPtr objcRegisterThreadWithCollector = dlsym( dlhandle, "objc_registerThreadWithCollector" );
1010			if( objcRegisterThreadWithCollector != NULL )
1011				objcRegisterThreadWithCollector();
1012			else syslog(LOG_INFO, "dlsym(objc_registerThreadWithCollector) failed");
1013			dlclose( dlhandle );
1014		}
1015		else syslog(LOG_INFO, "dlopen(/usr/lib/libobjc.dylib) failed");
1016	}
1017#endif	// REGISTER_THREADS_WITH_GC
1018
1019    CFRunLoopRef loop;
1020    //syslog(LOG_INFO, "Starting thread: %p\n", thread);
1021
1022#ifdef kIDH_Verbose_Debug_Logging
1023	syslog(LOG_INFO, "DVLib: DVRLThreadStart begin\n");
1024#endif
1025
1026     loop = CFRunLoopGetCurrent();
1027    //printf("Starting thread: %p, loop %p, notify retain %d, notify %p ioport %p, info %x\n",
1028    //    thread, loop, retain, thread->fNotifySource, thread->fNotifyPort, *((UInt32 *)thread->fNotifySource + 1));
1029    if(thread->fNotifySource)
1030        CFRunLoopAddSource(loop, thread->fNotifySource, kCFRunLoopDefaultMode);
1031
1032    if(thread->fPowerNotifySource)
1033        CFRunLoopAddSource(loop, thread->fPowerNotifySource, kCFRunLoopDefaultMode);
1034
1035
1036    CFRetain(loop);
1037    thread->fWorkLoop = loop;
1038
1039	thread->fRunLoopIsRunning = true;
1040
1041    // signal that we're about to start the runloop
1042    DVSignalSync(&thread->fRequestSyncer, &thread->fSyncRequest, 1);
1043
1044    CFRunLoopRun();
1045
1046	thread->fRunLoopIsRunning = false;
1047
1048    //printf("Exiting thread: %p, loop %p\n", thread, loop);
1049
1050#ifdef kIDH_Verbose_Debug_Logging
1051	syslog(LOG_INFO, "DVLib: DVRLThreadStart end\n");
1052#endif
1053
1054    return NULL;
1055}
1056
1057void
1058PowerManagementNotificationCallback(void * refcon,
1059									io_service_t service,
1060									natural_t messageType,
1061									void * messageArgument )
1062{
1063	DVThread *dvThread = (DVThread*) refcon;
1064	UInt32 i;
1065
1066#ifdef kIDH_Verbose_Debug_Logging
1067	syslog(LOG_INFO, "DVLib: PowerManagementNotificationCallback begin\n");
1068#endif
1069
1070	// If we are waking from sleep, restart any running streams
1071	if (messageType == kIOMessageSystemHasPoweredOn)
1072	{
1073		// Find all active streams, and restart them
1074        for(i=0; i<kDVMaxStreamsActive; i++)
1075		{
1076            if(dvThread->fInStreams[i])
1077			{
1078				syslog(LOG_INFO, "DV PowerManagementNotificationCallback, Restarting input stream %d\n",i);
1079				if (dvThread->fInStreams[i]->dvReadStopInProgress == false)
1080				{
1081					dvThread->fInStreams[i]->pendingDVReadUnderrunHandler = true;
1082					DVRequest(dvThread->fInStreams[i]->fStreamVars.fThread,
1083						doDVReadHandleInputUnderrun,
1084						dvThread->fInStreams[i],
1085						0);
1086				}
1087			}
1088
1089            if(dvThread->fOutStreams[i])
1090			{
1091				syslog(LOG_INFO, "DV PowerManagementNotificationCallback, Restarting output stream %d\n",i);
1092				if (dvThread->fOutStreams[i]->dvWriteStopInProgress == false)
1093				{
1094					dvThread->fOutStreams[i]->pendingDVWriteUnderrunHandler = true;
1095					DVRequest(dvThread->fOutStreams[i]->fStreamVars.fThread,
1096						doDVHandleOutputUnderrun,
1097						dvThread->fOutStreams[i],
1098						0);
1099				}
1100			}
1101        }
1102	}
1103
1104	// Acknowledge the message
1105	IOAllowPowerChange (dvThread->fPowerNotifyConnect, (long) messageArgument);
1106
1107#ifdef kIDH_Verbose_Debug_Logging
1108	syslog(LOG_INFO, "DVLib: PowerManagementNotificationCallback end\n");
1109#endif
1110
1111}
1112
1113DVThread * DVCreateThread(DVDeviceArrivedFunc deviceAdded, void * addedRefCon,
1114    CFRunLoopTimerCallBack timerTick, void *timerRefCon, IOFWAVCMessageCallback deviceMessage)
1115{
1116    UInt32 i;
1117    IOReturn err;
1118	mach_port_t masterDevicePort;
1119    DVThread *dvThread;
1120
1121    const UInt8 num = kAVCTapeRecorder;
1122    CFMutableDictionaryRef	dict = 0;
1123    CFNumberRef	tape;
1124
1125#ifdef kIDH_Verbose_Debug_Logging
1126	syslog(LOG_INFO, "DVLib: DVCreateThread begin\n");
1127#endif
1128
1129    dict = CFDictionaryCreateMutable( kCFAllocatorDefault, 0,
1130        &kCFTypeDictionaryKeyCallBacks,
1131        &kCFTypeDictionaryValueCallBacks);
1132
1133    if(!dict)
1134        return nil;
1135
1136    tape = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt8Type, &num);
1137    if(!tape)
1138        return nil;
1139
1140    CFDictionarySetValue( dict, CFSTR(kIOProviderClassKey), CFSTR("IOFireWireAVCSubUnit") );
1141    CFDictionarySetValue( dict, CFSTR("SubUnit_Type"), tape);
1142    CFRelease(tape);
1143
1144    if ((err = IOMasterPort(bootstrap_port, &masterDevicePort)) != KERN_SUCCESS) {
1145
1146#ifdef kIDH_Verbose_Debug_Logging
1147		syslog(LOG_INFO, "DVLib: DVCreateThread end:failed to get master port\n");
1148#endif
1149		return NULL;
1150    }
1151
1152    dvThread = malloc(sizeof(DVThread));
1153    bzero(dvThread, sizeof(DVThread));
1154    for(i = 0 ; i < kDVMaxDevicesActive ; i++){
1155        dvThread->fDevices[i].fOutPlug = kNoPlug;
1156    }
1157    pthread_mutex_init(&dvThread->fRequestSyncer.fMutex, NULL);
1158    pthread_cond_init(&dvThread->fRequestSyncer.fSyncCond, NULL);
1159    pthread_mutex_init(&dvThread->fRequestMutex, NULL);
1160
1161    dvThread->fNotifyPort = IONotificationPortCreate(masterDevicePort);
1162    dvThread->fNotifySource = IONotificationPortGetRunLoopSource(dvThread->fNotifyPort);
1163
1164    err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &dvThread->fRequestMachPort);
1165    err = mach_port_insert_right(mach_task_self(), dvThread->fRequestMachPort, dvThread->fRequestMachPort,
1166                                    MACH_MSG_TYPE_MAKE_SEND);
1167
1168    if(timerTick) {
1169        dvThread->fTimerFunc = timerTick;
1170        dvThread->fTimerRefCon = timerRefCon;
1171    }
1172
1173    dvThread->fAddedRefCon = addedRefCon;
1174    dvThread->fAddedFunc = deviceAdded;
1175    dvThread->fDeviceMessage = deviceMessage;
1176
1177    err = IOServiceAddMatchingNotification( dvThread->fNotifyPort,
1178            kIOMatchedNotification, dict,
1179            deviceArrived, dvThread, &dvThread->fMatchEnumer );
1180
1181	// Register for system power notifications
1182	dvThread->fPowerNotifyConnect = IORegisterForSystemPower ( dvThread,
1183							&dvThread->fPowerNotifyPort,
1184							PowerManagementNotificationCallback,
1185							&dvThread->fPowerManagementNotifier);
1186    dvThread->fPowerNotifySource = IONotificationPortGetRunLoopSource(dvThread->fPowerNotifyPort);
1187
1188#ifdef kIDH_Verbose_Debug_Logging
1189	syslog(LOG_INFO, "DVLib: DVCreateThread end\n");
1190#endif
1191
1192	return dvThread;
1193}
1194
1195static void setThreadPriority(pthread_t thread)
1196{
1197    double   mult;
1198    thread_time_constraint_policy_data_t constraints;
1199    kern_return_t result;
1200
1201#ifdef kIDH_Verbose_Debug_Logging
1202	syslog(LOG_INFO, "DVLib: setThreadPriority begin\n");
1203#endif
1204
1205	// use mach_timebase_info to get abs to ns conversion parameters
1206	mach_timebase_info_data_t tTBI;
1207	mach_timebase_info(&tTBI);
1208
1209    // Set thread to Real Time
1210	mult = ((double)tTBI.denom / (double)tTBI.numer) * 1000000;
1211    constraints.period = 12*mult;
1212    constraints.computation = 2*mult;
1213    constraints.constraint = 24*mult;
1214    constraints.preemptible = TRUE;
1215    result = thread_policy_set(pthread_mach_thread_np(thread), THREAD_TIME_CONSTRAINT_POLICY,
1216        (thread_policy_t)&constraints, THREAD_TIME_CONSTRAINT_POLICY_COUNT);
1217
1218#ifdef kIDH_Verbose_Debug_Logging
1219	syslog(LOG_INFO, "DVLib: setThreadPriority end\n");
1220#endif
1221
1222}
1223
1224void DVSetTimeoutTime(DVThread * dvThread, CFAbsoluteTime fireDate)
1225{
1226    dvThread->setTimeoutTime = CFAbsoluteTimeGetCurrent();
1227    dvThread->requestTimeoutTime = fireDate;
1228}
1229
1230void DVRunThread(DVThread * dvThread)
1231{
1232    pthread_attr_t threadAttr;			// Attributes of work thread
1233    pthread_t thread;
1234
1235#ifdef kIDH_Verbose_Debug_Logging
1236	syslog(LOG_INFO, "DVLib: DVRunThread begin\n");
1237#endif
1238
1239    // Start each thread, wait for first to start before setting up second.
1240    dvThread->fSyncRequest = 0;
1241    pthread_attr_init(&threadAttr);
1242    //pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED);
1243    pthread_create(&thread, &threadAttr, DVRLThreadStart, dvThread);
1244    dvThread->fRLThread = thread;
1245    DVWaitSync(&dvThread->fRequestSyncer, &dvThread->fSyncRequest);
1246
1247    dvThread->fSyncRequest = 0;
1248    pthread_create(&thread, &threadAttr, DVRTThreadStart, dvThread);
1249    dvThread->fRTThread = thread;
1250    setThreadPriority(thread);
1251    DVWaitSync(&dvThread->fRequestSyncer, &dvThread->fSyncRequest);
1252
1253#ifdef kIDH_Verbose_Debug_Logging
1254	syslog(LOG_INFO, "DVLib: DVRunThread end\n");
1255#endif
1256
1257}
1258
1259void DVFreeThread(DVThread * dvThread)
1260{
1261
1262#ifdef kIDH_Verbose_Debug_Logging
1263	syslog(LOG_INFO, "DVLib: DVFreeThread begin\n");
1264#endif
1265
1266    DVRequest(dvThread, DVthreadExit, dvThread, 0);
1267    pthread_join(dvThread->fRTThread, NULL);
1268    pthread_join(dvThread->fRLThread, NULL);
1269
1270    //printf("Workloop retain %d\n", CFGetRetainCount(dvThread->fWorkLoop));
1271    CFRelease(dvThread->fWorkLoop);
1272
1273    //CFRunLoopRemoveSource(dvThread->fWorkLoop, dvThread->fNotifySource, kCFRunLoopDefaultMode);
1274    //CFRunLoopRemoveSource(dvThread->fWorkLoop, dvThread->fRequestSource, kCFRunLoopDefaultMode);
1275   	//printf("after thread exit, notify , request retains are %d, %d\n",
1276    //    CFGetRetainCount(dvThread->fNotifySource), CFGetRetainCount(dvThread->fRequestSource));
1277    if(dvThread->fMatchEnumer)
1278        IOObjectRelease(dvThread->fMatchEnumer);
1279    if(dvThread->fNotifyPort) {
1280        CFMachPortRef hack;
1281        CFMachPortContext 	context;
1282        Boolean		shouldFreeInfo;
1283
1284        context.version = 1;
1285        context.info = (void *) dvThread->fNotifyPort;
1286        context.retain = NULL;
1287        context.release = NULL;
1288        context.copyDescription = NULL;
1289
1290
1291        hack = CFMachPortCreateWithPort(NULL, IONotificationPortGetMachPort(dvThread->fNotifyPort),
1292            NULL, &context, &shouldFreeInfo);
1293        CFMachPortInvalidate(hack);
1294        IONotificationPortDestroy(dvThread->fNotifyPort);
1295        //printf("hack port retain %d\n", CFGetRetainCount(hack));
1296        CFRelease(hack);
1297
1298
1299	}
1300
1301	if(dvThread->fPowerNotifyPort) {
1302        CFMachPortRef hack;
1303        CFMachPortContext 	context;
1304        Boolean		shouldFreeInfo;
1305
1306        context.version = 1;
1307        context.info = (void *) dvThread->fPowerNotifyPort;
1308        context.retain = NULL;
1309        context.release = NULL;
1310        context.copyDescription = NULL;
1311
1312
1313        hack = CFMachPortCreateWithPort(NULL, IONotificationPortGetMachPort(dvThread->fPowerNotifyPort),
1314										NULL, &context, &shouldFreeInfo);
1315		printf( "DVFreeThread - CFMachPortCreateWithPort hack = %p, fPowerNotifyPort= %p\n", hack, dvThread->fPowerNotifyPort );
1316
1317		CFMachPortInvalidate(hack);
1318        IONotificationPortDestroy(dvThread->fPowerNotifyPort);
1319        //printf("hack port retain %d\n", CFGetRetainCount(hack));
1320        CFRelease(hack);
1321
1322
1323	}
1324
1325	//IONotificationPortDestroy(dvThread->fPowerNotifyPort);
1326
1327    mach_port_destroy(mach_task_self(), dvThread->fRequestMachPort);
1328
1329    //printf("after IONotificationPortDestroy, notify retain is %d\n", CFGetRetainCount(dvThread->fNotifySource));
1330
1331    pthread_mutex_destroy(&dvThread->fRequestSyncer.fMutex);
1332    pthread_cond_destroy(&dvThread->fRequestSyncer.fSyncCond);
1333    pthread_mutex_destroy(&dvThread->fRequestMutex);
1334
1335	IODeregisterForSystemPower(&dvThread->fPowerManagementNotifier);
1336	IOServiceClose(dvThread->fPowerNotifyConnect);
1337    memset(dvThread, 0xde, sizeof(DVThread));
1338    free(dvThread);
1339
1340#ifdef kIDH_Verbose_Debug_Logging
1341	syslog(LOG_INFO, "DVLib: DVFreeThread end\n");
1342#endif
1343
1344}
1345
1346void DVSignalSync(ThreadSyncer *sync, UInt32 *var, UInt32 val)
1347{
1348#ifdef kIDH_Verbose_Debug_Logging
1349	syslog(LOG_INFO, "DVLib: DVSignalSync begin\n");
1350#endif
1351
1352    pthread_mutex_lock(&sync->fMutex);
1353    *var = val;
1354	pthread_mutex_unlock(&sync->fMutex);
1355    pthread_cond_broadcast(&sync->fSyncCond);
1356
1357#ifdef kIDH_Verbose_Debug_Logging
1358	syslog(LOG_INFO, "DVLib: DVSignalSync end\n");
1359#endif
1360}
1361
1362void DVWaitSync(ThreadSyncer *sync, UInt32 *var)
1363{
1364
1365#ifdef kIDH_Verbose_Debug_Logging
1366	syslog(LOG_INFO, "DVLib: DVWaitSync begin\n");
1367#endif
1368
1369    //if(!*var)
1370    {
1371        pthread_mutex_lock(&sync->fMutex);
1372        while(!*var) {
1373            pthread_cond_wait(&sync->fSyncCond, &sync->fMutex);
1374        }
1375        pthread_mutex_unlock(&sync->fMutex);
1376    }
1377
1378#ifdef kIDH_Verbose_Debug_Logging
1379	syslog(LOG_INFO, "DVLib: DVWaitSync end\n");
1380#endif
1381
1382}
1383
1384void DVLock(ThreadSyncer *sync)
1385{
1386
1387#ifdef kIDH_Verbose_Debug_Logging
1388	syslog(LOG_INFO, "DVLib: DVLock begin\n");
1389#endif
1390
1391    pthread_mutex_lock(&sync->fMutex);
1392
1393#ifdef kIDH_Verbose_Debug_Logging
1394	syslog(LOG_INFO, "DVLib: DVLock end\n");
1395#endif
1396}
1397
1398void DVUnlock(ThreadSyncer *sync)
1399{
1400
1401#ifdef kIDH_Verbose_Debug_Logging
1402	syslog(LOG_INFO, "DVLib: DVUnlock begin\n");
1403#endif
1404
1405    pthread_mutex_unlock(&sync->fMutex);
1406
1407#ifdef kIDH_Verbose_Debug_Logging
1408	syslog(LOG_INFO, "DVLib: DVUnlock end\n");
1409#endif
1410}
1411
1412static IOReturn isochPortGetSupported(
1413	IOFireWireLibIsochPortRef			interface,
1414	IOFWSpeed*							outMaxSpeed,
1415	UInt64*								outChanSupported)
1416{
1417    DVStream *stream;
1418
1419#ifdef kIDH_Verbose_Debug_Logging
1420	syslog(LOG_INFO, "DVLib: isochPortGetSupported begin\n");
1421#endif
1422
1423    stream = (DVStream *)((*interface)->GetRefCon(interface));
1424
1425    if(*outMaxSpeed > stream->fMaxSpeed)
1426        *outMaxSpeed = stream->fMaxSpeed;
1427    *outChanSupported = stream->fChannelMask;
1428
1429#ifdef kIDH_Verbose_Debug_Logging
1430	syslog(LOG_INFO, "DVLib: isochPortGetSupported end\n");
1431#endif
1432
1433	return kIOReturnSuccess;
1434}
1435
1436static IOReturn isochPortAllocate(
1437    IOFireWireLibIsochPortRef		interface,
1438	IOFWSpeed						maxSpeed,
1439	UInt32							channel)
1440{
1441    DVStream *stream;
1442
1443#ifdef kIDH_Verbose_Debug_Logging
1444	syslog(LOG_INFO, "DVLib: isochPortAllocate begin\n");
1445#endif
1446
1447    stream = (DVStream *)((*interface)->GetRefCon(interface));
1448    //printf("using channel %d\n", channel);
1449    stream->fIsocChannel = channel;
1450
1451#ifdef kIDH_Verbose_Debug_Logging
1452	syslog(LOG_INFO, "DVLib: isochPortAllocate end\n");
1453#endif
1454
1455	return kIOReturnSuccess;
1456}
1457
1458IOReturn openFireWireUnit(IOFireWireAVCLibUnitInterface **avcInterface, IOFireWireSessionRef session, IOFireWireLibDeviceRef *retInterface, DVThread *thread)
1459{
1460    IOFireWireLibDeviceRef	resultInterface;
1461    IOReturn				err = kIOReturnNoMemory;
1462    int						opened = false;
1463
1464#ifdef kIDH_Verbose_Debug_Logging
1465	syslog(LOG_INFO, "DVLib: openFireWireUnit begin\n");
1466#endif
1467
1468    do {
1469        resultInterface = (*avcInterface)->getAncestorInterface(avcInterface, "IOFireWireUnit",
1470            CFUUIDGetUUIDBytes(kIOFireWireLibTypeID), CFUUIDGetUUIDBytes(kIOFireWireUnitInterfaceID_v3));
1471        if(!resultInterface)
1472            break;
1473
1474        if(session)
1475            err = (*resultInterface)->OpenWithSessionRef(resultInterface, session);
1476        else
1477            err = (*resultInterface)->Open(resultInterface);
1478        if(err)
1479            break;
1480        opened = true;
1481        //err = (*resultInterface)->AddCallbackDispatcherToRunLoop(resultInterface, workLoop );
1482        err = (*resultInterface)->AddIsochCallbackDispatcherToRunLoop(resultInterface, thread->fWorkLoop);
1483    } while (false);
1484
1485    if(!err)
1486        *retInterface = resultInterface;
1487    else {
1488        if(opened)
1489            (*resultInterface)->Close(resultInterface);
1490        if(resultInterface)
1491            (*resultInterface)->Release(resultInterface);
1492    }
1493
1494#ifdef kIDH_Verbose_Debug_Logging
1495	syslog(LOG_INFO, "DVLib: openFireWireUnit end\n");
1496#endif
1497
1498    return err;
1499}
1500
1501IOReturn openAVCUnit(io_object_t obj, IOFireWireAVCLibUnitInterface ***retInterface, DVThread *thread)
1502{
1503    IOCFPlugInInterface** 	theCFPlugInInterface;
1504    IOFireWireAVCLibUnitInterface	**resultInterface = 0 ;
1505    SInt32					theScore ;
1506    IOReturn				err;
1507
1508#ifdef kIDH_Verbose_Debug_Logging
1509	syslog(LOG_INFO, "DVLib: openAVCUnit begin\n");
1510#endif
1511
1512	err = IOCreatePlugInInterfaceForService(
1513                    obj,
1514                    kIOFireWireAVCLibUnitTypeID,
1515                    kIOCFPlugInInterfaceID,		//interfaceType,
1516                    & theCFPlugInInterface,
1517                    & theScore);
1518    if (!err) {
1519        HRESULT comErr;
1520        comErr = (*theCFPlugInInterface)->QueryInterface(
1521                                            theCFPlugInInterface,
1522                                            CFUUIDGetUUIDBytes(kIOFireWireAVCLibUnitInterfaceID),
1523                                            (void**) & resultInterface);
1524        if (comErr == S_OK) {
1525            err = (*resultInterface)->addCallbackDispatcherToRunLoop(resultInterface, thread->fWorkLoop );
1526        }
1527        else
1528            err = comErr;
1529       (*theCFPlugInInterface)->Release(theCFPlugInInterface);	// Leave just one reference.
1530    }
1531
1532    if(!err)
1533        *retInterface = resultInterface;
1534
1535#ifdef kIDH_Verbose_Debug_Logging
1536	syslog(LOG_INFO, "DVLib: openAVCUnit end\n");
1537#endif
1538
1539    return err;
1540}
1541
1542IOReturn openAVCProto(IOFireWireAVCLibUnitInterface **avcInterface, IOFireWireAVCLibProtocolInterface ***retInterface, DVThread *thread)
1543{
1544    IOFireWireAVCLibProtocolInterface **resultInterface;
1545    IOReturn				err = noErr;
1546
1547#ifdef kIDH_Verbose_Debug_Logging
1548	syslog(LOG_INFO, "DVLib: openAVCProto begin\n");
1549#endif
1550
1551    do {
1552        resultInterface = (*avcInterface)->getProtocolInterface(avcInterface,
1553            CFUUIDGetUUIDBytes(kIOFireWireAVCLibProtocolTypeID),
1554            CFUUIDGetUUIDBytes(kIOFireWireAVCLibProtocolInterfaceID));
1555        if(!resultInterface)
1556            break;
1557        err = (*resultInterface)->addCallbackDispatcherToRunLoop(resultInterface, thread->fWorkLoop);
1558    } while (false);
1559
1560    if(!err)
1561        *retInterface = resultInterface;
1562    else {
1563        if(resultInterface)
1564            (*resultInterface)->Release(resultInterface);
1565    }
1566
1567
1568#ifdef kIDH_Verbose_Debug_Logging
1569	syslog(LOG_INFO, "DVLib: openAVCProto end\n");
1570#endif
1571
1572	return err;
1573}
1574
1575void DVDeviceTerminate(DVDevice *dev)
1576{
1577
1578#ifdef kIDH_Verbose_Debug_Logging
1579	syslog(LOG_INFO, "DVLib: DVDeviceTerminate begin\n");
1580#endif
1581
1582    DVDeviceClose(dev);
1583    if(dev->fAVCInterface)
1584	{
1585		// Remove runloop source for this interface
1586		(*dev->fAVCInterface)->removeCallbackDispatcherFromRunLoop(dev->fAVCInterface);
1587
1588		// Crasher Fix: If we are on a thread other than the RL thread,
1589		// we need to make sure the RL thread is idle before
1590		// releasing the AVC interface
1591		if (dev->fThread->fWorkLoop != CFRunLoopGetCurrent())
1592		{
1593			// Endless loop here until the run-loop thread is idle
1594			while ((CFRunLoopIsWaiting(dev->fThread->fWorkLoop) == false ) && (dev->fThread->fRunLoopIsRunning == true))
1595				usleep(1000); // sleep for a millisecond
1596		}
1597
1598        (*dev->fAVCInterface)->Release(dev->fAVCInterface);
1599        dev->fAVCInterface = NULL;
1600    }
1601    if(dev->fObject) {
1602        IOObjectRelease(dev->fObject);
1603        dev->fObject = NULL;
1604    }
1605
1606#ifdef kIDH_Verbose_Debug_Logging
1607	syslog(LOG_INFO, "DVLib: DVDeviceTerminate end\n");
1608#endif
1609
1610}
1611
1612IOReturn DVDeviceOpen(DVThread *dvThread, DVDevice *device)
1613{
1614    IOReturn err = noErr;
1615
1616#ifdef kIDH_Verbose_Debug_Logging
1617	syslog(LOG_INFO, "DVLib: DVDeviceOpen begin\n");
1618#endif
1619
1620    if(!device->fAVCInterface)
1621        return kIOReturnNoMemory;
1622
1623    do {
1624        err = (*device->fAVCInterface)->open(device->fAVCInterface);
1625
1626        if(err != kIOReturnSuccess) break;
1627        err = openFireWireUnit(device->fAVCInterface, (*device->fAVCInterface)->getSessionRef(device->fAVCInterface),
1628                    &device->fDevInterface, dvThread);
1629        if(err != kIOReturnSuccess) break;
1630
1631        err = openAVCProto(device->fAVCInterface, &device->fAVCProtoInterface, dvThread);
1632        if(err != kIOReturnSuccess) break;
1633        err = (*device->fAVCProtoInterface)->allocateOutputPlug(device->fAVCProtoInterface,
1634                                                    device, handlePCRLock, &device->fOutPlug);
1635        if(err != kIOReturnSuccess) break;
1636
1637		err = writePlug(device->fAVCProtoInterface, device->fOutPlug, 122 << kIOFWPCROutputPayloadPhase);
1638        if(err != kIOReturnSuccess) break;
1639    } while (0);
1640    if(err != kIOReturnSuccess)
1641        DVDeviceClose(device);
1642
1643#ifdef kIDH_Verbose_Debug_Logging
1644	syslog(LOG_INFO, "DVLib: DVDeviceOpen end\n");
1645#endif
1646
1647	return err;
1648}
1649
1650static IOReturn doDVDeviceClose(DVDevice *dev)
1651{
1652
1653#ifdef kIDH_Verbose_Debug_Logging
1654	syslog(LOG_INFO, "DVLib: doDVDeviceClose begin\n");
1655#endif
1656
1657	if(dev->fDevInterface) {
1658        UInt32 ref;
1659        (*dev->fDevInterface)->Close(dev->fDevInterface);
1660
1661		// Remove isoch callback runloop source
1662		(*dev->fDevInterface)->RemoveIsochCallbackDispatcherFromRunLoop(dev->fDevInterface);
1663
1664        ref = (*dev->fDevInterface)->Release(dev->fDevInterface);
1665        //syslog(LOG_INFO, "DVCloseDriver FW refcount was %d\n", ref);
1666        dev->fDevInterface = NULL;
1667    }
1668
1669    if(dev->fAVCProtoInterface)
1670	{
1671        UInt32 ref;
1672        if(dev->fOutPlug != kNoPlug) {
1673            (*dev->fAVCProtoInterface)->freeOutputPlug(dev->fAVCProtoInterface, dev->fOutPlug);
1674            dev->fOutPlug = kNoPlug;
1675        }
1676
1677		// Remove callback runloop source
1678		(*dev->fAVCProtoInterface)->removeCallbackDispatcherFromRunLoop(dev->fAVCProtoInterface);
1679
1680        ref = (*dev->fAVCProtoInterface)->Release(dev->fAVCProtoInterface);
1681        //syslog(LOG_INFO, "DVCloseDriver AVCproto refcount was %d\n", ref);
1682        dev->fAVCProtoInterface = NULL;
1683    }
1684
1685    if(dev->fAVCInterface) {
1686        (*dev->fAVCInterface)->close(dev->fAVCInterface);
1687    }
1688
1689#ifdef kIDH_Verbose_Debug_Logging
1690	syslog(LOG_INFO, "DVLib: doDVDeviceClose end\n");
1691#endif
1692
1693	return kIOReturnSuccess;
1694}
1695
1696void DVDeviceClose(DVDevice *dev)
1697{
1698
1699#ifdef kIDH_Verbose_Debug_Logging
1700	syslog(LOG_INFO, "DVLib: DVDeviceClose begin\n");
1701#endif
1702
1703	DVRequest(dev->fThread, doDVDeviceClose, dev, 0);
1704
1705#ifdef kIDH_Verbose_Debug_Logging
1706	syslog(LOG_INFO, "DVLib: DVDeviceClose end\n");
1707#endif
1708
1709}
1710
1711IOReturn DVRequest(DVThread *thread, IOReturn (*func)(void *arg, UInt32 param), void *arg, UInt32 param)
1712{
1713    IOReturn result;
1714
1715    //printf("Doing request %p\n", func);
1716    if(thread->fRTThread != pthread_self()) {
1717
1718        pthread_mutex_lock(&thread->fRequestMutex);
1719        thread->fSyncRequest = 0;
1720        thread->fRequestFunc = func;
1721        thread->fRequestArg = arg;
1722        thread->fRequestParam = param;
1723
1724        {
1725            SendMsg msg;
1726            bzero( &msg, sizeof(msg));
1727
1728            msg.msgHdr.msgh_remote_port	= thread->fRequestMachPort;
1729            msg.msgHdr.msgh_bits = MACH_MSGH_BITS(
1730                                                    MACH_MSG_TYPE_COPY_SEND,
1731                                                    MACH_MSG_TYPE_COPY_SEND );
1732            msg.msgHdr.msgh_size 	= sizeof(msg);
1733            msg.msgHdr.msgh_id		= kDVRequestID;
1734
1735            mach_msg(&msg.msgHdr, MACH_SEND_MSG,
1736                        msg.msgHdr.msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
1737        }
1738
1739        DVWaitSync(&thread->fRequestSyncer, &thread->fSyncRequest);
1740        result = thread->fRequestResult;
1741        pthread_mutex_unlock(&thread->fRequestMutex);
1742    }
1743    else
1744        result = (*func)(arg, param);
1745
1746    return result;
1747}
1748
1749static void initStream(DVStream *stream, DVDevice *device, UInt32 plug, UInt32 channel, DVThread *thread)
1750{
1751
1752#ifdef kIDH_Verbose_Debug_Logging
1753	syslog(LOG_INFO, "DVLib: initStream begin\n");
1754#endif
1755
1756	stream->pFWDevice = device->fDevInterface;
1757    stream->pDVDevice = device;
1758    stream->fAVCProtoInterface = device->fAVCProtoInterface;
1759    stream->fPlug = plug;
1760    stream->fIsocChannel = channel;
1761    stream->fMaxSpeed = device->fMaxSpeed;
1762    stream->fThread = thread;
1763
1764#ifdef kIDH_Verbose_Debug_Logging
1765	syslog(LOG_INFO, "DVLib: initStream end\n");
1766#endif
1767
1768}
1769
1770static IOReturn openStream(DVStream *stream, bool forWrite, UInt32 packetSize)
1771{
1772    IOReturn err;
1773    IOFireWireLibIsochPortRef talker, listener;
1774    IOVirtualRange bufRange;
1775    bool allocBandwidth;
1776
1777#ifdef kIDH_Verbose_Debug_Logging
1778	syslog(LOG_INFO, "DVLib: openStream begin\n");
1779#endif
1780
1781    do {
1782
1783        if(forWrite) {
1784            // always allocate bandwidth
1785            allocBandwidth = true;
1786        }
1787        else {
1788            // Figure out if the device is already tranmitting, in which case use that channel and don't
1789            // allocate bandwidth
1790            UInt32 plugVal, plugValHost;
1791            io_object_t obj;
1792            FWAddress addr;
1793            UInt32 size;
1794            // Use any channel not already in use, or the channel that the camcorder is already using
1795            addr.nodeID = 0;
1796            addr.addressHi = 0xffff;
1797            addr.addressLo = 0xf0000904;
1798            size = 4;
1799            obj = (*stream->pFWDevice)->GetDevice(stream->pFWDevice);
1800            err = (*stream->pFWDevice)->ReadQuadlet(stream->pFWDevice, obj, &addr, &plugVal, false, 0);
1801			plugValHost = EndianU32_BtoN( plugVal );
1802
1803            if(plugValHost & (kIOFWPCRBroadcast | kIOFWPCRP2PCount)) {
1804                UInt32 chan = (plugValHost & kIOFWPCRChannel)>>kIOFWPCRChannelPhase;
1805                //printf("Already transmitting on channel %x\n", chan);
1806                stream->fChannelMask = 1ULL << (63-chan);
1807                allocBandwidth = false;
1808            }
1809            else {
1810#ifdef USE_P2P_CONNECTIONS_FOR_DV_READ
1811                stream->fChannelMask = ~1ULL;
1812                allocBandwidth = true;
1813#else
1814                stream->fChannelMask = 1ULL;	// Assume the camera will use channel 63
1815                allocBandwidth = false;
1816#endif
1817            }
1818        }
1819        stream->fIsochChannelRef = (*stream->pFWDevice)->CreateIsochChannel(stream->pFWDevice, allocBandwidth, packetSize,
1820            stream->fMaxSpeed, CFUUIDGetUUIDBytes(kIOFireWireIsochChannelInterfaceID));
1821        if (NULL == stream->fIsochChannelRef) {
1822            err = memFullErr;
1823            break;
1824        }
1825
1826        bufRange.address = (IOVirtualAddress)stream->fDCLBuffers;
1827        bufRange.length = stream->fDCLBufferSize;
1828
1829        // Add local node as talker or listener
1830        if(forWrite) {
1831            stream->pFWLocalIsochPort = (*stream->pFWDevice)->CreateLocalIsochPort(stream->pFWDevice, 1 /*inTalking*/,
1832                            stream->pDCLList, kFWDCLCycleEvent, 0, 0x0000f000, nil, 0, &bufRange, 1,
1833                                            CFUUIDGetUUIDBytes(kIOFireWireLocalIsochPortInterfaceID));
1834
1835            // Use any available channel
1836            stream->fChannelMask = ~1ULL;
1837        }
1838        else {
1839            stream->pFWLocalIsochPort = (*stream->pFWDevice)->CreateLocalIsochPort(stream->pFWDevice, 0 /*inTalking*/,
1840                            stream->pDCLList, 0, 0, 0, nil, 0, &bufRange, 1,
1841                                            CFUUIDGetUUIDBytes(kIOFireWireLocalIsochPortInterfaceID));
1842        }
1843		if (!stream->pFWLocalIsochPort) {
1844			err = memFullErr;
1845            break;
1846        }
1847
1848		stream->pFWRemoteIsochPort = (*stream->pFWDevice)->CreateRemoteIsochPort(stream->pFWDevice, 0,										CFUUIDGetUUIDBytes(kIOFireWireRemoteIsochPortInterfaceID) );
1849
1850        (*stream->pFWRemoteIsochPort)->SetRefCon( stream->pFWRemoteIsochPort, stream);
1851        (*stream->pFWRemoteIsochPort)->SetGetSupportedHandler( stream->pFWRemoteIsochPort, &isochPortGetSupported);
1852        (*stream->pFWRemoteIsochPort)->SetAllocatePortHandler( stream->pFWRemoteIsochPort, &isochPortAllocate);
1853
1854        if(forWrite) {
1855            talker = (IOFireWireLibIsochPortRef) stream->pFWLocalIsochPort;
1856            listener = (IOFireWireLibIsochPortRef)stream->pFWRemoteIsochPort;
1857        }
1858        else {
1859            listener = (IOFireWireLibIsochPortRef) stream->pFWLocalIsochPort;
1860            talker = (IOFireWireLibIsochPortRef)stream->pFWRemoteIsochPort;
1861        }
1862		err = (*stream->fIsochChannelRef)->SetTalker( stream->fIsochChannelRef, talker);
1863        if(err)
1864            break;
1865		err = (*stream->fIsochChannelRef)->AddListener( stream->fIsochChannelRef, listener);
1866        if(err)
1867            break;
1868
1869
1870		// Initialize isochronous channel.
1871
1872        (*stream->fIsochChannelRef)->TurnOnNotification(stream->fIsochChannelRef);
1873		err = (*stream->fIsochChannelRef)->AllocateChannel(stream->fIsochChannelRef);
1874        if(err)
1875            break;
1876        if(forWrite) {
1877            // set our output plug broadcast bit, channel number and bandwidth usage.
1878            err = writePlug(stream->fAVCProtoInterface, stream->fPlug,
1879                kIOFWPCROnline | kIOFWPCRBroadcast | (1 << kIOFWPCRP2PCountPhase) |
1880                (stream->fIsocChannel<<kIOFWPCRChannelPhase) |
1881                (15 << kIOFWPCROutputOverheadPhase) | (122 << kIOFWPCROutputPayloadPhase));
1882
1883            if(err)
1884                break;
1885            err = MakeP2PConnectionForWrite(stream->pDVDevice,0,stream->fIsocChannel);
1886        }
1887        else
1888        {
1889#ifdef USE_P2P_CONNECTIONS_FOR_DV_READ
1890            err = MakeP2PConnectionForRead(stream->pDVDevice,0,stream->fIsocChannel);
1891#endif
1892        }
1893
1894        err = (*stream->fIsochChannelRef)->Start(stream->fIsochChannelRef);
1895        if(err)
1896            break;
1897
1898        stream->fFrames.fStatus = kDVRunning;
1899
1900    } while (false);
1901
1902	// If we got any errors, call closeStream now to cleanup
1903	if(err)
1904		closeStream(stream);
1905
1906#ifdef kIDH_Verbose_Debug_Logging
1907	syslog(LOG_INFO, "DVLib: openStream end\n");
1908#endif
1909
1910    return err;
1911}
1912
1913static void closeStream(DVStream *stream)
1914{
1915    IOReturn err;
1916
1917#ifdef kIDH_Verbose_Debug_Logging
1918	syslog(LOG_INFO, "DVLib: closeStream begin\n");
1919#endif
1920
1921    stream->fFrames.fStatus = kDVStopped;
1922    if(stream->fIsochChannelRef) {
1923        (*stream->fIsochChannelRef)->TurnOffNotification(stream->fIsochChannelRef);
1924        //syslog(LOG_INFO, "Stopping stream %p\n", stream);
1925        err = (*stream->fIsochChannelRef)->Stop(stream->fIsochChannelRef);
1926        //syslog(LOG_INFO, "Stopped stream, err %x\n", err);
1927        //syslog(LOG_INFO, "ReleaseChannel of stream %p\n", stream);
1928        err = (*stream->fIsochChannelRef)->ReleaseChannel(stream->fIsochChannelRef);
1929        //syslog(LOG_INFO, "releaseChannelled, err %x\n", err);
1930        //syslog(LOG_INFO, "Releaseing channel of stream %p\n", stream);
1931        (*stream->fIsochChannelRef)->Release(stream->fIsochChannelRef);
1932        stream->fIsochChannelRef = NULL;
1933    }
1934    if(stream->pFWLocalIsochPort) {
1935        (*stream->pFWLocalIsochPort)->Release(stream->pFWLocalIsochPort);
1936        stream->pFWLocalIsochPort = NULL;
1937    }
1938    if(stream->pFWRemoteIsochPort) {
1939        (*stream->pFWRemoteIsochPort)->Release(stream->pFWRemoteIsochPort);
1940        stream->pFWRemoteIsochPort = NULL;
1941    }
1942
1943    // Run the runloop for .1 secs to pick up stray DCL callbacks
1944    // But we don't want to run the other runloop sources...
1945    //CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
1946
1947#ifdef kIDH_Verbose_Debug_Logging
1948	syslog(LOG_INFO, "DVLib: closeStream end\n");
1949#endif
1950
1951}
1952
1953static IOReturn DVAllocFrames(DVFrameVars *pFrameData, UInt32 numFrames, UInt32 frameSize,
1954        DVFrameVars **frameVars, UInt8 **frames)
1955{
1956    int i;
1957
1958#ifdef kIDH_Verbose_Debug_Logging
1959	syslog(LOG_INFO, "DVLib: DVAllocFrames begin\n");
1960#endif
1961
1962    pFrameData->fNumFrames = numFrames;
1963    pFrameData->fFrames = malloc(numFrames*frameSize);
1964    pFrameData->fReader = 0;
1965    pFrameData->fWriter = 0;
1966    pFrameData->fDroppedFrames = 0;
1967    pFrameData->fStatus = 0;
1968    for(i=0; i<numFrames; i++) {
1969        frames[i] = pFrameData->fFrames + i*frameSize;
1970    }
1971    *frameVars = pFrameData;
1972
1973#ifdef kIDH_Verbose_Debug_Logging
1974	syslog(LOG_INFO, "DVLib: DVAllocFrames end\n");
1975#endif
1976
1977    return kIOReturnSuccess;
1978}
1979
1980static void DVFreeFrames(DVFrameVars *pFrameData)
1981{
1982
1983#ifdef kIDH_Verbose_Debug_Logging
1984	syslog(LOG_INFO, "DVLib: DVFreeFrames begin\n");
1985#endif
1986
1987    if(!pFrameData->fFrames)
1988	{
1989#ifdef kIDH_Verbose_Debug_Logging
1990		syslog(LOG_INFO, "DVLib: DVFreeFrames end:no frames\n");
1991#endif
1992        return;
1993	}
1994
1995    free(pFrameData->fFrames);
1996    pFrameData->fFrames = NULL;
1997
1998#ifdef kIDH_Verbose_Debug_Logging
1999	syslog(LOG_INFO, "DVLib: DVFreeFrames end\n");
2000#endif
2001}
2002
2003static void DVGetNextFullOutputFrame(DVFrameVars *pFrameData, UInt8** ppFrame, UInt32 frameSize )
2004{
2005    if(NULL == *ppFrame) {
2006        *ppFrame = pFrameData->fFrames;
2007    }
2008    else {
2009        if (pFrameData->fReader + 1 < pFrameData->fWriter) {
2010            pFrameData->fReader++;
2011        }
2012        else {
2013            //syslog(LOG_INFO, "DVGetNextFullOutputFrame: dropping frame: reader %d writer %d dropped %d\n",
2014                //pFrameData->fReader, pFrameData->fWriter,
2015                //pFrameData->fDroppedFrames);
2016            pFrameData->fDroppedFrames++;
2017        }
2018        *ppFrame = pFrameData->fFrames +
2019            frameSize*(pFrameData->fReader % pFrameData->fNumFrames);
2020    }
2021}
2022
2023void DVSetInputFrameSizeAndMode(DVFrameVars *pFrameData, UInt32 bytes, UInt8 mode, UInt32 frameTime )
2024{
2025    int index = pFrameData->fWriter % pFrameData->fNumFrames;
2026    int i;
2027    pFrameData->fFrameSize[index] = bytes;
2028    pFrameData->fFrameStandard[index] = mode;
2029    pFrameData->fFrameTime[index] = frameTime;
2030    pFrameData->fFrameStatus[index] = kReady;
2031
2032    // find next free frame
2033    for(i=pFrameData->fWriter + 1; i < pFrameData->fReader + pFrameData->fNumFrames; i++) {
2034        if(pFrameData->fFrameStatus[i % pFrameData->fNumFrames] != kReading)
2035            break;
2036        //syslog(LOG_INFO, "Skipping frame %d (%d) state %d\n",
2037        //    i, i % pFrameData->fNumFrames, pFrameData->fFrameStatus[i % pFrameData->fNumFrames]);
2038    }
2039    if (i< pFrameData->fReader + pFrameData->fNumFrames)
2040        pFrameData->fWriter = i;
2041    else {
2042        pFrameData->fDroppedFrames++;
2043        //(LOG_INFO, "Dropping frame on input, dropped %d @ %d\n",
2044        //    pFrameData->fDroppedFrames, pFrameData->fWriter);
2045    }
2046}
2047
2048void DVGetNextEmptyInputFrame(DVFrameVars *pFrameData,  UInt8** ppFrame, UInt32 frameSize )
2049{
2050    int index = pFrameData->fWriter % pFrameData->fNumFrames;
2051    *ppFrame = pFrameData->fFrames + frameSize*index;
2052	pFrameData->fFrameStatus[index] = kWriting;
2053}
2054
2055static UInt32 getEmptyPacketsPerGroup(DVGlobalOutPtr pGlobalData, UInt32 numDataPacketsPerPlayBufferGroup)
2056{
2057    // Compute the number of data packets per empty packet.
2058    // If the frame rate is expressed as n/d, the number of data packets per buffer group
2059    // expressed as A, and the number of data packets per frame as C, then the number of
2060    // empty packets per buffer group B should be
2061    //
2062    // B = int (8000*d/n*A/C - A + 1)
2063    // B = A*((8000*d)/(n*c) - 1) + 1
2064
2065	//
2066    // in order to ensure that the frame rate may be maintained by periodically reducing
2067    // the number of empty packets in a buffer group by 1.
2068    //
2069
2070	UInt32 numEmptyPacketsPerPlayBufferGroup;
2071    UInt32 A1, C1, d1, n1;
2072
2073    A1 = numDataPacketsPerPlayBufferGroup;
2074    C1 = pGlobalData->numDataPacketsPerFrame;
2075    n1 = pGlobalData->playFrameRateNumerator;
2076    d1 = pGlobalData->playFrameRateDenominator;
2077#if ALT_TIMING
2078	{
2079		UInt32 d2 = C1*n1;
2080		UInt32 n2 = (8000 * d1 * A1) - (d2 * A1) + d2;
2081		numEmptyPacketsPerPlayBufferGroup = n2 / d2;
2082	}
2083#else
2084    numEmptyPacketsPerPlayBufferGroup = (8000 * d1 * A1 + (n1 * C1)/2) / (n1 * C1) - A1;
2085#endif
2086
2087    return numEmptyPacketsPerPlayBufferGroup;
2088}
2089
2090static void FreeDCLCommandPool(DVGlobalOutPtr pGlobalData)
2091{
2092
2093#ifdef kIDH_Verbose_Debug_Logging
2094	syslog(LOG_INFO, "DVLib: FreeDCLCommandPool begin\n");
2095#endif
2096
2097    if( pGlobalData->fDCLCommandPool != NULL ) {
2098        free(pGlobalData->fDCLCommandPool);
2099        pGlobalData->fDCLCommandPool = NULL;
2100    }
2101
2102#ifdef kIDH_Verbose_Debug_Logging
2103	syslog(LOG_INFO, "DVLib: FreeDCLCommandPool end\n");
2104#endif
2105
2106}
2107
2108static IOReturn AllocateDCLCommandPool(DVGlobalOutPtr pGlobalData, UInt32 total )
2109{
2110    UInt8 * pDCLCommandPool;
2111
2112#ifdef kIDH_Verbose_Debug_Logging
2113	syslog(LOG_INFO, "DVLib: AllocateDCLCommandPool begin\n");
2114#endif
2115
2116    // Allocate DCL command pool record.
2117    pDCLCommandPool = malloc(total);
2118    if (pDCLCommandPool == NULL)
2119    {
2120        // syslog(LOG_INFO, "AllocateDCLCommandPool: IOMalloc: pDCLCommandPool failed\n");
2121#ifdef kIDH_Verbose_Debug_Logging
2122		syslog(LOG_INFO, "DVLib: AllocateDCLCommandPool end:no pool\n");
2123#endif
2124        return kIOReturnNoMemory;
2125    }
2126    else
2127    {
2128        pGlobalData->fTotalPool = total;
2129        pGlobalData->fAllocatedPool = 0;
2130        pGlobalData->fDCLCommandPool = pDCLCommandPool;
2131    }
2132
2133#ifdef kIDH_Verbose_Debug_Logging
2134	syslog(LOG_INFO, "DVLib: AllocateDCLCommandPool end\n");
2135#endif
2136
2137    return kIOReturnSuccess;
2138}
2139
2140static DCLCommandPtr AllocateDCLCommand(DVGlobalOutPtr pGlobalData, UInt32 dclSize )
2141{
2142    DCLCommandPtr pDCLCommand;
2143
2144    if(pGlobalData->fAllocatedPool + dclSize <= pGlobalData->fTotalPool) {
2145        pDCLCommand = (DCLCommandPtr)(pGlobalData->fDCLCommandPool + pGlobalData->fAllocatedPool);
2146        pGlobalData->fAllocatedPool += dclSize;
2147    }
2148    else {
2149        syslog(LOG_INFO, "Trying to allocated DCL command size %d, no space left\n", dclSize);
2150        pDCLCommand = NULL;
2151    }
2152
2153    return (pDCLCommand);
2154}
2155
2156static DVLocalOutPtr DVAllocatePlayBufferGroup(DVGlobalOutPtr pGlobalData, int num)
2157{
2158    DVLocalOutPtr pLocalData,
2159                  pPrevLocalData,
2160                  pNextLocalData;
2161
2162    // Allocate buffer group data record.
2163    pLocalData = &pGlobalData->fLocalDataArray[num];
2164    pLocalData->pGlobalData = pGlobalData;
2165    pLocalData->fBlockNum = num;
2166
2167    // Insert buffer group data record into list.
2168    if(num == 0) {
2169        pPrevLocalData = &pGlobalData->fLocalDataArray[kNumPlayBufferGroups-1];
2170    }
2171    else
2172        pPrevLocalData = &pGlobalData->fLocalDataArray[num-1];
2173
2174    if(num == kNumPlayBufferGroups-1)
2175        pNextLocalData = &pGlobalData->fLocalDataArray[0];
2176    else
2177        pNextLocalData = &pGlobalData->fLocalDataArray[num+1];
2178
2179    pLocalData->pNextLocalData = pNextLocalData;
2180    pLocalData->pPrevLocalData = pPrevLocalData;
2181    return (pLocalData);
2182}
2183
2184static void DVDeallocatePlayBufferGroup( DVLocalOutPtr pLocalData )
2185{
2186    if ( pLocalData != NULL )
2187    {
2188        if ( pLocalData->bufferGroupUpdateDCLList != NULL )
2189            free(pLocalData->bufferGroupUpdateDCLList);
2190   }
2191}
2192
2193static IOReturn  DVCreatePlayBufferGroupUpdateList( DVLocalOutPtr pLocalData)
2194{
2195    DCLCommandPtr     pDCLCommand,
2196                      pLastDCLCommand;
2197    DCLCommandPtr     *updateDCLList,
2198                      *pUpdateDCLListEntry;
2199    UInt32            opcode;
2200    UInt32            updateListSize;
2201    IOReturn          error = 0;
2202
2203    // Loop through all DCL commands in buffer group and count all send packet DCL
2204    // commands.
2205    pDCLCommand = pLocalData->pFirstBufferGroupDCLCommand;
2206    pLastDCLCommand = pLocalData->pLastBufferGroupDCLCommand;
2207    updateListSize = 0;
2208    while (pDCLCommand != pLastDCLCommand)
2209    {
2210        opcode = pDCLCommand->opcode & ~kFWDCLOpFlagMask;
2211        if ((opcode == kDCLSendPacketStartOp) || (opcode == kDCLSendPacketOp))
2212            updateListSize++;
2213
2214        pDCLCommand = pDCLCommand->pNextDCLCommand;
2215    }
2216    opcode = pDCLCommand->opcode & ~kFWDCLOpFlagMask;
2217    if ((opcode == kDCLSendPacketStartOp) || (opcode == kDCLSendPacketOp))
2218        updateListSize++;
2219
2220    // Allocate update list.
2221    updateDCLList = (DCLCommandPtr *)malloc( updateListSize * sizeof (DCLCommandPtr) );
2222    if (updateDCLList == NULL)
2223    {
2224        // syslog(LOG_INFO, "DVCreatePlayBufferGroupUpdateList: IOMalloc: updateDCLList\n");
2225        error = kIOReturnNoMemory;
2226    }
2227    else
2228    {
2229        bzero( updateDCLList, updateListSize * sizeof (DCLCommandPtr) );
2230    }
2231
2232    // Loop through all DCL commands in buffer group and add all send packet DCL
2233    // commands to update list.
2234    if (error == 0)
2235    {
2236        pDCLCommand = pLocalData->pFirstBufferGroupDCLCommand;
2237        pLastDCLCommand = pLocalData->pLastBufferGroupDCLCommand;
2238        pUpdateDCLListEntry = updateDCLList;
2239
2240        while (pDCLCommand != pLastDCLCommand)
2241        {
2242            opcode = pDCLCommand->opcode & ~kFWDCLOpFlagMask;
2243            if ((opcode == kDCLSendPacketStartOp) || (opcode == kDCLSendPacketOp))
2244                *pUpdateDCLListEntry++ = pDCLCommand;
2245
2246            pDCLCommand = pDCLCommand->pNextDCLCommand;
2247        }
2248
2249        opcode = pDCLCommand->opcode & ~kFWDCLOpFlagMask;
2250        if ((opcode == kDCLSendPacketStartOp) || (opcode == kDCLSendPacketOp))
2251            *pUpdateDCLListEntry++ = pDCLCommand;
2252    }
2253
2254    // Save update list.
2255    if (error == 0)
2256    {
2257        pLocalData->bufferGroupUpdateDCLList = updateDCLList;
2258        pLocalData->updateListSize = updateListSize;
2259    }
2260    else
2261    {
2262        pLocalData->bufferGroupUpdateDCLList = NULL;
2263        pLocalData->updateListSize = 0;
2264    }
2265
2266    return ( error );
2267}
2268
2269static void ModifyDCLJump(IOFireWireLibLocalIsochPortRef port, DCLJumpPtr pDCLJump, DCLLabelPtr pDCLLabel)
2270{
2271    // Send notification to DCL compiler.
2272    if (port) {
2273        (*port)->ModifyJumpDCL( port, pDCLJump, pDCLLabel);
2274    }
2275}
2276
2277void DVSilenceFrame(UInt8 mode, UInt8* frame)
2278{
2279    UInt32    i,j,k,n;
2280    UInt8    *tPtr;
2281	UInt8    sType = ((mode & 0x7C) >> 2);
2282
2283    //syslog(LOG_INFO, "silencing frame %p\n", frame);
2284
2285    // Get DSF flag in byte 3 of header (Blue Book p. 113)
2286    tPtr = frame;
2287    if ((tPtr[3] & 0x80) == 0)
2288        n=10;                            // ntsc
2289    else
2290        n=12;                            // pal
2291
2292	if (sType == 1)
2293		n /= 2;							//  SDL
2294	else if (sType == 0x1D)
2295		n *= 2;							// DVCPro-50
2296
2297    // Mute all the audio samples
2298
2299    for (i=0;i<n;i++)
2300    {
2301        for (j=0;j<9;j++)
2302        {
2303            tPtr = frame + (i * 12000) + ((j * 16 + 6) * 80) + 8;
2304            for (k=0;k<72;k++)
2305                *tPtr++ = 0x0;
2306        }
2307    }
2308}
2309
2310
2311static void DVUpdateOutputBuffers( DVLocalOutPtr pLocalData )
2312{
2313    DCLCommandPtr         pCurrentDCLCommand;
2314    DCLTransferPacketPtr  pDCLTransferPacket;
2315    DVLocalOutPtr		pPrevLocalData;
2316    DVGlobalOutPtr        pGlobalData;
2317    UInt16                localNodeID;
2318    UInt32				  shiftedNodeID;	// Poistioned for ORing into header0
2319    UInt32                nominalFrameCycleTime;
2320    UInt32                syt;
2321    UInt32                *pBuffer, *pImageBuffer, *pLastImageBuffer;
2322    UInt32                packetNum, dataPacketNum, numPackets;
2323    UInt32                dbc;
2324//    static UInt16       lastFrameSequence = 0;
2325//    UInt16              currentFrameSequence;
2326	UInt8 stype;
2327
2328    // Get driver data and first DCL command.
2329    pGlobalData = pLocalData->pGlobalData;
2330
2331    pCurrentDCLCommand = pLocalData->pFirstBufferGroupDCLCommand;
2332    nominalFrameCycleTime = pGlobalData->nominalFrameCycleTime;
2333
2334    // Get data for previous buffer group.
2335    pPrevLocalData = pLocalData->pPrevLocalData;
2336    syt = pGlobalData->nextSYT;
2337    dbc = pGlobalData->nextDBC;
2338    dataPacketNum = pGlobalData->nextDataPacketNum;
2339
2340    // Get local node ID.
2341
2342    (*pGlobalData->fStreamVars.pFWDevice)->GetLocalNodeID(pGlobalData->fStreamVars.pFWDevice, &localNodeID);
2343    localNodeID &= 0x3f;
2344    shiftedNodeID = (UInt32)localNodeID << 24;
2345
2346    // Get first send packet command for this buffer group.
2347    while (pCurrentDCLCommand->opcode != kDCLSendPacketStartOp)
2348        pCurrentDCLCommand = pCurrentDCLCommand->pNextDCLCommand;
2349    pDCLTransferPacket = (DCLTransferPacketPtr) pCurrentDCLCommand;
2350
2351    // Update the packet buffers.
2352    numPackets = pLocalData->numPackets;
2353
2354    if(pGlobalData->fUpdateBuffers) {
2355        // Get the next frame to output
2356        if( pGlobalData->pImageBuffer == NULL ) {
2357            DVGetNextFullOutputFrame(&pGlobalData->fStreamVars.fFrames,
2358							(UInt8 **)&(pGlobalData->pImageBuffer),
2359							 pGlobalData->fStreamVars.fDVFrameSize);
2360        }
2361    }
2362    pImageBuffer = ( pGlobalData->pImageBuffer + (pGlobalData->fDataQuadSize * dataPacketNum) );
2363    for( packetNum = 0; packetNum < numPackets; packetNum++)
2364    {
2365        // Set up packet header.
2366        pBuffer = (UInt32 *) pDCLTransferPacket->buffer;
2367
2368        pBuffer[0] = EndianU32_NtoB( pGlobalData->fHeader0 | (dbc & 0xFF) | shiftedNodeID );
2369        pBuffer[1] = EndianU32_NtoB( pGlobalData->fHeader1 | 0xFFFF );
2370
2371        // if not an empty packet
2372        if (pDCLTransferPacket->size > kDVPacketCIPSize)
2373        {
2374            // Set SYT field if this is the first data packet in the frame.
2375            if (dataPacketNum == 0)
2376            {
2377                pBuffer[1] = EndianU32_NtoB( pGlobalData->fHeader1 | (syt & 0xFFFF) );
2378                syt = AddFWCycleTimeToFWCycleTime(syt, pGlobalData->nominalFrameCycleTime);
2379            }
2380
2381            // Copy data into packet.
2382            if(pGlobalData->fUpdateBuffers) {
2383                bcopy(pImageBuffer, (void *)((UInt32)(pDCLTransferPacket->buffer) + kDVPacketCIPSize),
2384                        pGlobalData->fDataPacketSize);
2385                pImageBuffer += pGlobalData->fDataQuadSize;
2386            }
2387
2388			// Increment dbc based on stream type
2389			// TODO: This will need to change to support 2x,4x modes on some signal types
2390			stype = pGlobalData->fStreamVars.fSignalMode & kAVCSignalModeMask_STYPE;
2391			switch (stype)
2392			{
2393				case kAVCSignalModeMask_DVCPro50:
2394					dbc += 2;	// DBC increments by two for each packet
2395					break;
2396
2397				case kAVCSignalModeMask_DVCPro100:
2398					dbc += 4;	// DBC increments by four for each packet
2399					break;
2400
2401				case kAVCSignalModeMask_SDL:
2402				case kAVCSignalModeMask_DVCPro25:
2403				case kAVCSignalModeMask_HD:
2404				default:	// SD video stream
2405					dbc += 1;	// DBC increments by one for each packet
2406					break;
2407			};
2408            dataPacketNum++;
2409
2410            // check if frame is done
2411            if (dataPacketNum == pGlobalData->numDataPacketsPerFrame )
2412            {
2413                // syslog(LOG_INFO, "frame done\n");
2414                // DVCIsochCompleteEvent    theEvent;
2415
2416                // post a DV event to let the curious know...
2417                // theEvent.eventHeader.deviceID     = gpDVFWData->deviceID;
2418                // theEvent.eventHeader.theEvent     = kDVIsochWriteComplete;
2419                // theEvent.pFrameBuffer            = (Ptr) pImageBuffer;
2420                // theEvent.fwCycleTime            = syt;
2421                // FIXME: DVCPostEvent( (DVCEventRecordPtr) &theEvent );
2422                // syslog(LOG_INFO, "DVCPostEvent\n");
2423
2424                // pImageBuffer = (UInt32 *) pDVCDriverData->imageBuffer;
2425                dataPacketNum = 0;
2426                //pDVCDriverData->playData.imageBuffer = GetNextOutputFrame();
2427
2428                if(pGlobalData->fUpdateBuffers) {
2429                    pLastImageBuffer = pGlobalData->pImageBuffer;
2430
2431                    DVGetNextFullOutputFrame(&pGlobalData->fStreamVars.fFrames,
2432								(UInt8 **)&(pGlobalData->pImageBuffer),
2433								 pGlobalData->fStreamVars.fDVFrameSize);
2434                    pImageBuffer = pGlobalData->pImageBuffer;
2435
2436                    // Mute the audio on repeating frames, based on repeating frame sequences
2437                    if (pImageBuffer == pLastImageBuffer)
2438                        DVSilenceFrame(pGlobalData->fStreamVars.fSignalMode, (UInt8 *)pImageBuffer);
2439                }	// End if(pGlobalData->fUpdateBuffers)
2440            } // end if end frame
2441        } // end if empty
2442
2443        // Find next send packet start command.
2444        pCurrentDCLCommand = pCurrentDCLCommand->pNextDCLCommand;
2445        while (pCurrentDCLCommand != NULL)
2446        {
2447            if (pCurrentDCLCommand->opcode != kDCLSendPacketStartOp)
2448                pCurrentDCLCommand = pCurrentDCLCommand->pNextDCLCommand;
2449            else
2450                break;
2451        }
2452        pDCLTransferPacket = (DCLTransferPacketPtr) pCurrentDCLCommand;
2453    }
2454    pGlobalData->nextSYT = syt;
2455    pGlobalData->nextDBC = dbc;
2456    pGlobalData->nextDataPacketNum = dataPacketNum;
2457
2458}
2459
2460static void DVHandleOutput(DVLocalOutPtr pLocalData)
2461{
2462    DVGlobalOutPtr     pGlobalData;
2463    DVLocalOutPtr      pPrevLocalData;
2464    UInt32             nominalFrameCycleTime;
2465    UInt32             fractionalFrameCycleCount,
2466                       fractionalFrameCycleOffset;
2467    SInt32             timeDrift;
2468    UInt32             cycleDrift;
2469    UInt32             projectedTimeStamp,
2470                       projectedSYT;
2471#if TIMING
2472    CFAbsoluteTime cstart, cend;
2473    cstart = CFAbsoluteTimeGetCurrent();
2474#endif
2475
2476    //syslog(LOG_INFO, "DVHandleOutput: 0x%x\n", pDCLCommandPtr);
2477    pPrevLocalData = pLocalData->pPrevLocalData;
2478    pGlobalData = pLocalData->pGlobalData;
2479    nominalFrameCycleTime = pGlobalData->nominalFrameCycleTime;
2480
2481    // Undo skipping empty packet if we're currently skipping a packet.
2482    if (pLocalData->skippingEmptyPacket)
2483    {
2484        ModifyDCLJump(pGlobalData->fStreamVars.pFWLocalIsochPort,
2485            pLocalData->pBufferGroupSkipEmptyPacketDCLJump, pLocalData->pBufferGroupDontSkipEmptyPacketDCLLabel);
2486        pGlobalData->activePackets++;
2487        pLocalData->skippingEmptyPacket = false;
2488    }
2489
2490    // Compute time drift.
2491
2492    // Compute the projected time stamp value for the first packet of the current
2493    // buffer group the next time this proc is called for the current buffer group.
2494
2495    // Start at time stamp of first packet in next buffer group to be sent.
2496    projectedTimeStamp = *pLocalData->pBufferGroupTimeStampPtr;	// Time last packet in group was sent last time round
2497    projectedTimeStamp = AddFWCycleTimeToFWCycleTime(projectedTimeStamp, 1 << 12);
2498
2499    // Add the total number of cycles for all active buffer group packets.
2500    projectedTimeStamp = AddFWCycleTimeToFWCycleTime(projectedTimeStamp, pGlobalData->activePackets << 12);
2501
2502    // Subtract the number of cycles for all packets in the current buffer group.
2503    projectedTimeStamp = SubtractFWCycleTimeFromFWCycleTime(projectedTimeStamp, pLocalData->numPackets << 12);
2504
2505    // Compute the projected SYT value for the first packet of the current buffer group
2506    // the next time this proc is called for the current buffer group.
2507
2508    // Start with the SYT value to use for the first packet of the next frame.
2509    projectedSYT = pGlobalData->nextSYT;
2510
2511    // Subtract the SYT offset between frames if we aren't at the start of a frame
2512    if(pGlobalData->nextDataPacketNum != 0) {
2513        projectedSYT = SubtractFWCycleTimeFromFWCycleTime(projectedSYT, nominalFrameCycleTime);
2514
2515        // Add the fraction of the SYT offset between the start of the frame and the
2516        // first data packet for the current buffer group.
2517        fractionalFrameCycleOffset =
2518            ((nominalFrameCycleTime & 0x0FFF) * pGlobalData->nextDataPacketNum) /
2519            pGlobalData->numDataPacketsPerFrame;
2520
2521        fractionalFrameCycleCount =
2522            ((nominalFrameCycleTime & 0x01FFF000) * pGlobalData->nextDataPacketNum) /
2523            pGlobalData->numDataPacketsPerFrame;
2524        fractionalFrameCycleCount =
2525            (fractionalFrameCycleCount & 0x01FFF000) +
2526            (((fractionalFrameCycleCount & 0x0FFF) * 3072) / 4096);
2527
2528        projectedSYT = AddFWCycleTimeToFWCycleTime (projectedSYT, fractionalFrameCycleOffset);
2529        projectedSYT = AddFWCycleTimeToFWCycleTime (projectedSYT, fractionalFrameCycleCount);
2530    }
2531
2532    // The time drift is the difference between the projected time stamp and SYT.
2533    // We must convert the time drift to cycles.
2534    cycleDrift = AddFWCycleTimeToFWCycleTime(projectedTimeStamp, kPlaySYTDelay << 12);
2535    cycleDrift = SubtractFWCycleTimeFromFWCycleTime(cycleDrift, projectedSYT);
2536//syslog(LOG_INFO, "time drift %x group %p\n", cycleDrift >> 12, pLocalData );
2537    timeDrift = (cycleDrift >> 12) & 0x000F;
2538    // Skip an empty packet if we're drifting.
2539    // Only consider positive drifting.
2540    if ((timeDrift > 0) && (timeDrift < 0x0008))
2541    {
2542        ModifyDCLJump(pGlobalData->fStreamVars.pFWLocalIsochPort,
2543            pLocalData->pBufferGroupSkipEmptyPacketDCLJump, pLocalData->pBufferGroupSkipEmptyPacketDCLLabel);
2544        pGlobalData->activePackets--;
2545        pLocalData->skippingEmptyPacket = true;
2546    }
2547
2548    DVUpdateOutputBuffers( pLocalData );
2549
2550    // Update DCL jumps to call underrun proc after this buffer group.
2551    //zzz check errors.
2552    ModifyDCLJump (pGlobalData->fStreamVars.pFWLocalIsochPort,
2553        pLocalData->pEndOfBufferGroupDCLJump, pGlobalData->pUnderrunDCLLabel);
2554    ModifyDCLJump (pGlobalData->fStreamVars.pFWLocalIsochPort,
2555        pPrevLocalData->pEndOfBufferGroupDCLJump, pLocalData->pStartOfBufferGroupDCLLabel);
2556    pGlobalData->fSharedDCLVars.fDMAPos = pLocalData->fBlockNum;
2557#if TIMING
2558    cend = CFAbsoluteTimeGetCurrent();
2559    DVLog(pGlobalData->fStreamVars.fThread, 'isoc', cstart, cend);
2560#endif
2561}
2562
2563static void DVWritePoll(DVGlobalOutPtr globs)
2564{
2565    int i, pos;
2566    pos = globs->fSharedDCLVars.fDMAPos;
2567    for(i=pos; i<kNumPlayBufferGroups; i++)
2568        if(*globs->fLocalDataArray[i].pBufferGroupTimeStampPtr != 0xffffffff) {
2569            DVHandleOutput(&globs->fLocalDataArray[i]);
2570            *globs->fLocalDataArray[i].pBufferGroupTimeStampPtr = 0xffffffff;
2571        }
2572
2573    for(i=0; i<pos; i++)
2574        if(*globs->fLocalDataArray[i].pBufferGroupTimeStampPtr != 0xffffffff) {
2575            DVHandleOutput(&globs->fLocalDataArray[i]);
2576            *globs->fLocalDataArray[i].pBufferGroupTimeStampPtr = 0xffffffff;
2577        }
2578}
2579
2580
2581static void doDVHandleOutputUnderrun(  DVGlobalOutPtr	pGlobalData )
2582{
2583    IOReturn		err;
2584    // FIXME
2585
2586    syslog(LOG_INFO, "DVHandleOutputUnderrun: 0x%p\n", pGlobalData);
2587
2588    DVStream *stream;
2589
2590    if ((pGlobalData->pendingDVWriteUnderrunHandler == true) && (pGlobalData->deferredDVWriteFree == true))
2591    {
2592	// Free the globalout data struct
2593	free(pGlobalData);
2594	return;
2595    }
2596    pGlobalData->pendingDVWriteUnderrunHandler = false;
2597
2598
2599    stream = &pGlobalData->fStreamVars;
2600
2601	// See if stream still open. If not, we're done!
2602	if (stream->fIsochChannelRef == NULL)
2603		return;
2604
2605	closeStream(&pGlobalData->fStreamVars);
2606
2607	FreeDCLCommandPool(pGlobalData);
2608
2609	BreakP2PConnectionForWrite(pGlobalData->fStreamVars.pDVDevice,0,pGlobalData->fStreamVars.fIsocChannel);
2610
2611    err = buildWriteProgram(pGlobalData);
2612    if(err != kIOReturnSuccess)
2613        syslog(LOG_INFO, "DVHandleOutputUnderrun: buildWriteProgram returned %x\n", err);
2614
2615	err = DVWriteStart(pGlobalData);
2616}
2617
2618static void DVHandleOutputUnderrun( DCLCommandPtr pDCLCommandPtr )
2619{
2620    DVGlobalOutPtr pGlobalData;
2621
2622#ifdef kIDH_Verbose_Debug_Logging
2623	syslog(LOG_INFO, "DVLib: DVHandleOutputUnderrun begin\n");
2624#endif
2625
2626    pGlobalData = (DVGlobalOutPtr)((DCLCallProcPtr)pDCLCommandPtr)->procData;
2627    if (pGlobalData->dvWriteStopInProgress == false)
2628    {
2629        pGlobalData->pendingDVWriteUnderrunHandler = true;
2630        DVRequest(pGlobalData->fStreamVars.fThread, doDVHandleOutputUnderrun, pGlobalData, 0);
2631    }
2632
2633#ifdef kIDH_Verbose_Debug_Logging
2634	syslog(LOG_INFO, "DVLib: DVHandleOutputUnderrun end\n");
2635#endif
2636}
2637
2638
2639static void DVDisposeDCLOutput( DVGlobalOutPtr pOutputData )
2640{
2641    DVLocalOutPtr  pLocalData, pNextLocalData;
2642    UInt32         bufferGroupNum;
2643
2644#ifdef kIDH_Verbose_Debug_Logging
2645	syslog(LOG_INFO, "DVLib: DVDisposeDCLOutput begin\n");
2646#endif
2647
2648    // syslog(LOG_INFO, "DVDisposeDCLOutput\n");
2649    if( pOutputData != NULL )
2650    {
2651        // Deallocate play buffer group data records.
2652        // and update lists associated with them
2653        pLocalData = &pOutputData->fLocalDataArray[0];
2654        for (bufferGroupNum = 0; bufferGroupNum < kNumPlayBufferGroups; bufferGroupNum++)
2655        {
2656            if( pLocalData != NULL )
2657            {
2658                pNextLocalData = pLocalData->pNextLocalData;
2659                DVDeallocatePlayBufferGroup (pLocalData);
2660                pLocalData = pNextLocalData;
2661            }
2662        }
2663
2664        FreeDCLCommandPool(pOutputData);
2665
2666        /* don't dispose of the frame buffer here.. wait since its shared
2667        DVReleaseFrameIO();
2668        */
2669
2670        if( pOutputData->fStreamVars.fDCLBuffers != NULL)
2671        {
2672            //free( pOutputData->pTransmitBuffers); //pOutputData->fDCLBufferSize );
2673            vm_deallocate(mach_task_self(), (vm_address_t)pOutputData->fStreamVars.fDCLBuffers,
2674                pOutputData->fStreamVars.fDCLBufferSize);
2675        }
2676        //free( pOutputData); //, sizeof(DVGlobalOut) );
2677    }
2678
2679#ifdef kIDH_Verbose_Debug_Logging
2680	syslog(LOG_INFO, "DVLib: DVDisposeDCLOutput end\n");
2681#endif
2682
2683}
2684
2685static IOReturn allocateBuffers(DVGlobalOutPtr pGlobalData)
2686{
2687    UInt32			numDataPacketsPerPage;
2688    UInt32			numEmptyPackets;
2689    UInt32			pageSize;
2690    UInt32			emptySize;		// Space used by empty packer headers.
2691    UInt32			transmitBuffersSize;
2692
2693    IOReturn		res;
2694
2695#ifdef kIDH_Verbose_Debug_Logging
2696	syslog(LOG_INFO, "DVLib: allocateBuffers begin\n");
2697#endif
2698
2699	// Setup CIP header static bits, plus packet size based on signal mode
2700	UInt8 stype = pGlobalData->fStreamVars.fSignalMode & kAVCSignalModeMask_STYPE;
2701	switch (stype)
2702	{
2703		case kAVCSignalModeMask_SDL:
2704			pGlobalData->fHeader0 = 0x003c0000;
2705			pGlobalData->fHeader1 = 0x80040000;
2706			pGlobalData->fDataPacketSize = kDVSDLPayloadPacketSize;			// Data portion, in bytes
2707			break;
2708
2709		case kAVCSignalModeMask_DVCPro25:
2710			pGlobalData->fHeader0 = 0x00780000;
2711			pGlobalData->fHeader1 = 0x80780000;
2712			pGlobalData->fDataPacketSize = kDVSDPayloadPacketSize;			// Data portion, in bytes
2713			break;
2714
2715		case kAVCSignalModeMask_DVCPro50:
2716			pGlobalData->fHeader0 = 0x00784000;
2717			pGlobalData->fHeader1 = 0x80740000;
2718			pGlobalData->fDataPacketSize = kDVCPro50PayloadPacketSize;			// Data portion, in bytes
2719			break;
2720
2721		case kAVCSignalModeMask_DVCPro100:
2722			pGlobalData->fHeader0 = 0x00788000;
2723			pGlobalData->fHeader1 = 0x80700000;
2724			pGlobalData->fDataPacketSize = kDVCPro50PayloadPacketSize;			// Data portion, in bytes
2725			break;
2726
2727		case kAVCSignalModeMask_HD:
2728			pGlobalData->fHeader0 = 0x00F00000;
2729			pGlobalData->fHeader1 = 0x80080000;
2730			pGlobalData->fDataPacketSize = kDVCPro50PayloadPacketSize;			// Data portion, in bytes
2731			break;
2732
2733		default:	// Must be SD video stream
2734			pGlobalData->fHeader0 = 0x00780000;
2735			pGlobalData->fHeader1 = 0x80000000;
2736			pGlobalData->fDataPacketSize = kDVSDPayloadPacketSize;			// Data portion, in bytes
2737			break;
2738	};
2739
2740    pGlobalData->fDataQuadSize = pGlobalData->fDataPacketSize/4;	// Data portion, in quads
2741    pGlobalData->fAlignQuadSize = (pGlobalData->fDataPacketSize + kDVPacketCIPSize + 15)/16;
2742    pGlobalData->fAlignQuadSize *= 4;					// Packet size padded out to 16 byte boundary, in quads
2743    pGlobalData->fSharedDCLVars.fAlignedPacketSize = 4*pGlobalData->fAlignQuadSize; //Packet size in bytes
2744    pGlobalData->fSharedDCLVars.fPacketDataSize = pGlobalData->fDataPacketSize;
2745
2746    ////////////////////////////////////////////////
2747    //
2748    // set timing values for appropriate video system
2749    //
2750    if( !(pGlobalData->fStreamVars.fSignalMode & kAVCSignalModeMask_50) )
2751    {
2752        //syslog(LOG_INFO, "NTSC output\n");
2753        pGlobalData->playFramePeriodNumerator = kNTSCPlayFramePeriodNumerator;
2754        pGlobalData->playFramePeriodDenominator = kNTSCPlayFramePeriodDenominator;
2755
2756        pGlobalData->playFrameRateNumerator = kNTSCFrameRateNumerator;
2757        pGlobalData->playFrameRateDenominator = kNTSCFrameRateDenominator;
2758
2759        pGlobalData->numDataPacketsPerFrame = kNTSCNumDataPacketsPerDVFrame;
2760        pGlobalData->numDataPacketsPerGroup = kNTSCNumDataPacketsPerGroup;
2761    }
2762    else
2763    {
2764        //syslog(LOG_INFO, "PAL output\n");
2765        pGlobalData->fHeader1 |= kPALBit;
2766        pGlobalData->playFramePeriodNumerator = kPALPlayFramePeriodNumerator;
2767        pGlobalData->playFramePeriodDenominator = kPALPlayFramePeriodDenominator;
2768
2769        pGlobalData->playFrameRateNumerator = kPALFrameRateNumerator;
2770        pGlobalData->playFrameRateDenominator = kPALFrameRateDenominator;
2771
2772        pGlobalData->numDataPacketsPerFrame = kPALNumDataPacketsPerDVFrame;
2773        pGlobalData->numDataPacketsPerGroup = kPALNumDataPacketsPerGroup;
2774    }
2775
2776    // Compute nominal frame period cycle time.
2777    pGlobalData->nominalFrameCycleTime = ConvertFractionalSecondsToFWCycleTime
2778        (pGlobalData->playFramePeriodNumerator, pGlobalData->playFramePeriodDenominator);
2779
2780    pGlobalData->fSharedDCLVars.fNumGroups = kNumPlayBufferGroups;
2781    pGlobalData->fSharedDCLVars.fGroupSize = pGlobalData->numDataPacketsPerGroup;
2782
2783    pageSize = PAGE_SIZE;
2784    numDataPacketsPerPage = pageSize /
2785                (pGlobalData->fDataPacketSize + kDVPacketCIPSize + kDVPacketAlignSlop);
2786#if ALT_TIMING
2787    numEmptyPackets = getEmptyPacketsPerGroup(pGlobalData, pGlobalData->numDataPacketsPerGroup) * kNumPlayBufferGroups;
2788#else
2789    numEmptyPackets =
2790        getEmptyPacketsPerGroup(pGlobalData, pGlobalData->numDataPacketsPerGroup * kNumPlayBufferGroups) + kNumPlayBufferGroups/2;
2791#endif
2792
2793    transmitBuffersSize = pGlobalData->numDataPacketsPerGroup * kNumPlayBufferGroups * pageSize;
2794    transmitBuffersSize /= numDataPacketsPerPage;
2795    // add pages for empty packets and time stamps
2796    emptySize = numEmptyPackets * (kDVPacketCIPSize + kDVPacketAlignSlop);
2797    // Allocate.
2798    //syslog(LOG_INFO, "DVWrite: IOMalloc: pGlobalData->pTransmitBuffers size %d, empty %d\n",transmitBuffersSize, emptySize);
2799    //pGlobalData->pTransmitBuffers = (UInt8 *)malloc( transmitBuffersSize + emptySize + pageSize );
2800    pGlobalData->fStreamVars.fDCLBufferSize =
2801                    transmitBuffersSize + emptySize + sizeof(UInt32)*kNumPlayBufferGroups;
2802    vm_allocate(mach_task_self(), (vm_address_t *)&pGlobalData->fStreamVars.fDCLBuffers,
2803       pGlobalData->fStreamVars.fDCLBufferSize, VM_FLAGS_ANYWHERE);
2804    //syslog(LOG_INFO, "DCL buffers at %p\n", pGlobalData->fStreamVars.fDCLBuffers);
2805    if( pGlobalData->fStreamVars.fDCLBuffers == NULL ) {
2806        res = kIOReturnNoMemory;
2807        goto bail;
2808    }
2809    bzero( pGlobalData->fStreamVars.fDCLBuffers, pGlobalData->fStreamVars.fDCLBufferSize );
2810    pGlobalData->pEmptyTransmitBuffers = pGlobalData->fStreamVars.fDCLBuffers + transmitBuffersSize;
2811    pGlobalData->fSharedDCLVars.fTimeStampPtrs = (UInt32 *)(pGlobalData->pEmptyTransmitBuffers + emptySize);
2812
2813#ifdef kIDH_Verbose_Debug_Logging
2814	syslog(LOG_INFO, "DVLib: allocateBuffers end\n");
2815#endif
2816
2817	return kIOReturnSuccess;
2818
2819bail:
2820    DVDisposeDCLOutput( pGlobalData );
2821
2822#ifdef kIDH_Verbose_Debug_Logging
2823	syslog(LOG_INFO, "DVLib: allocateBuffers end:fail\n");
2824#endif
2825
2826    return res;
2827}
2828
2829/*
2830    The write program is built of kNumPlayBufferGroups blocks of kDCLSendPacketStartOps,
2831    kNumDataPacketsPerPlayBufferGroup of the ops in each block send empty DV packets (just the CIP header) so that the timing
2832    comes out right.
2833    In addition, the callproc (DVHandleOutput) adjusts the blocks to skip an empty packet when the Mac is sending data too slowly.
2834
2835    LoopDCL:
2836    SetTagSyncBits(tag=1, sync=0)
2837
2838    kNumPlayBufferGroups*
2839        SendPacketStart (full packet/empty packet)
2840        or, for first empty packet in group:
2841            Jump	(initially to SendEmptyLabel, altered to SkipEmptyLabel when falling behind)
2842            SendEmptyLabel:
2843            SendPacketStart (empty packet)
2844            SkipEmptyLabel:
2845        Jump (initially to EndOfGroup, except last group jumps to Underrun)
2846        EndOfGroup:
2847        TimeStamp
2848        UpdateDCLList(TimeStamp)
2849        CallProc(DVHandleOutput)
2850        UpdateDCLList(All the SendPackets)
2851
2852    Jump (LoopDCL)
2853
2854    Underrun:
2855    CallProc(DVHandleOutputUnderrun)
2856*/
2857static IOReturn buildWriteProgram(DVGlobalOutPtr pGlobalData)
2858{
2859
2860    UInt32                        numEmptyPacketsInPlayBufferGroup;
2861
2862    DCLCommandPtr                pDCLCommand;
2863    DCLCommandPtr                pFirstBufferGroupDCLCommand;
2864    DCLLabelPtr                    pUnderrunDCLLabel,
2865                                pBufferGroupDCLLabel,
2866                                pDCLLabel;
2867    DCLTransferPacketPtr        pDCLTransferPacket;
2868    DCLCallProcPtr                pDCLCallProc;
2869    DCLSetTagSyncBitsPtr        pDCLSetTagSyncBits;
2870    DCLJumpPtr                    pDCLJump,
2871                                pBufferGroupDCLJump;
2872    DCLLabelPtr                    pBufferGroupSkipEmptyPacketDCLLabel;
2873    DCLUpdateDCLListPtr            pDCLUpdateDCLList;
2874    DCLPtrTimeStampPtr			pDCLTimeStamp;
2875
2876    DVLocalOutPtr                pPlayBufferGroupData;
2877    UInt32 *			pTransmitBuffer;
2878    UInt8 *				pEmptyTransmitBuffer;
2879    volatile UInt32 *	pTimeStampPtr;
2880    UInt32				bufferGroupNum;
2881    UInt32				dataPacketNum;
2882    UInt32				numPackets;
2883    UInt32              emptyPacketNumerator;
2884    UInt32				pageOffset;
2885    IOReturn			res;
2886
2887    UInt32			 totalDCLSize;
2888    UInt32			totalEmpty, emptySoFar;
2889
2890#ifdef kIDH_Verbose_Debug_Logging
2891	syslog(LOG_INFO, "DVLib: buildWriteProgram begin\n");
2892#endif
2893
2894#if ALT_TIMING
2895	totalEmpty = getEmptyPacketsPerGroup(pGlobalData, pGlobalData->numDataPacketsPerGroup) * kNumPlayBufferGroups;
2896#else
2897    totalEmpty = getEmptyPacketsPerGroup(pGlobalData, pGlobalData->numDataPacketsPerGroup * kNumPlayBufferGroups) + kNumPlayBufferGroups/2;
2898#endif
2899
2900    // syslog(LOG_INFO, "total empty %d\n", totalEmpty);
2901    emptySoFar = 0;
2902    pTransmitBuffer = (UInt32 *) pGlobalData->fStreamVars.fDCLBuffers;
2903    pEmptyTransmitBuffer = pGlobalData->pEmptyTransmitBuffers;
2904    pTimeStampPtr = pGlobalData->fSharedDCLVars.fTimeStampPtrs;
2905
2906    /////////////////////////////////////////
2907    // Start Up DCL Allocation Engine
2908    // Allocate DCL command pool.
2909    //
2910    // DCLs:
2911    // Start Label + SetTagSyncBits +  DCLJump + DCLLabel + DCLCallProc
2912    //    kNumPlayBufferGroups * ( (kNumDataPacketsPerPlayBufferGroup + numEmptyPacketsPerPlayBufferGroup) * DCLTransferPacket + 3 * DCLLabel + 2 * DCLJump + DCLPtrTimeStamp + DCLUpdateDCLList + DCLCallProc + DCLUpdateDCLList)
2913
2914    totalDCLSize = 2 * sizeof(DCLLabel) + sizeof(DCLSetTagSyncBits) + sizeof(DCLJump) + sizeof(DCLCallProc) +
2915    kNumPlayBufferGroups * (pGlobalData->numDataPacketsPerGroup * sizeof(DCLTransferPacket) + 3*sizeof(DCLLabel) + 2*sizeof(DCLJump) + sizeof(DCLPtrTimeStamp) + 2*sizeof(DCLUpdateDCLList) + sizeof(DCLCallProc) + totalEmpty * sizeof(DCLTransferPacket));
2916
2917    res = AllocateDCLCommandPool(pGlobalData, totalDCLSize);
2918    if (res)
2919        goto bail;
2920
2921    ////////////////////////////////////
2922    // Actually Create DCL Program
2923    //
2924
2925    // Initialize total packet count.
2926    pGlobalData->totalPackets = 0;
2927    pGlobalData->activePackets = 0;
2928
2929    // Set isoch packet tag bits to the way DV likes 'em
2930    pDCLSetTagSyncBits = (DCLSetTagSyncBitsPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLSetTagSyncBits));
2931    pDCLCommand = (DCLCommandPtr) pDCLSetTagSyncBits;
2932    pDCLSetTagSyncBits->opcode = kDCLSetTagSyncBitsOp;
2933    pDCLSetTagSyncBits->tagBits = 1;
2934    pDCLSetTagSyncBits->syncBits = 0;
2935
2936	// Set the pointer to the start of this DCL program
2937	pGlobalData->fStreamVars.pDCLList = pDCLCommand;
2938
2939    for (bufferGroupNum = 0; bufferGroupNum < kNumPlayBufferGroups; bufferGroupNum++)
2940    {
2941        // Allocate a buffer group data record.
2942        pPlayBufferGroupData = DVAllocatePlayBufferGroup( pGlobalData, bufferGroupNum);
2943
2944        // Initialize for loop.
2945        dataPacketNum = 0;
2946        numPackets = 0;
2947        emptyPacketNumerator = 0;
2948#if ALT_TIMING
2949        numEmptyPacketsInPlayBufferGroup = totalEmpty/kNumPlayBufferGroups;
2950#else
2951        numEmptyPacketsInPlayBufferGroup = (totalEmpty*(bufferGroupNum+1)+kNumPlayBufferGroups/2)/kNumPlayBufferGroups - emptySoFar;
2952#endif
2953        emptySoFar += numEmptyPacketsInPlayBufferGroup;
2954        pFirstBufferGroupDCLCommand = NULL;
2955        pBufferGroupSkipEmptyPacketDCLLabel = NULL;
2956        pGlobalData->fSharedDCLVars.fDataOffset[bufferGroupNum] =
2957                                (UInt8*)pTransmitBuffer - pGlobalData->fStreamVars.fDCLBuffers;
2958
2959        // Create label for start of buffer group.
2960        pBufferGroupDCLLabel = (DCLLabelPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLLabel));
2961        pPlayBufferGroupData->pStartOfBufferGroupDCLLabel = pBufferGroupDCLLabel;
2962        pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pBufferGroupDCLLabel;
2963        pDCLCommand = (DCLCommandPtr) pBufferGroupDCLLabel;
2964        pBufferGroupDCLLabel->opcode = kDCLLabelOp;
2965
2966		while (dataPacketNum < pGlobalData->numDataPacketsPerGroup)
2967        {
2968            // Send a packet: CIP header + payload.
2969            pDCLTransferPacket = (DCLTransferPacketPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLTransferPacket));
2970            pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLTransferPacket;
2971            pDCLCommand = (DCLCommandPtr) pDCLTransferPacket;
2972            pDCLTransferPacket->opcode = kDCLSendPacketStartOp;
2973            pDCLTransferPacket->size = pGlobalData->fDataPacketSize + kDVPacketCIPSize;
2974
2975            // check for buffer crossing page
2976            pageOffset = (UInt32) (pTransmitBuffer + pGlobalData->fAlignQuadSize) & 0x0fff;
2977            if (pageOffset < (4*pGlobalData->fAlignQuadSize) && pageOffset > 0)
2978            {
2979                // if it does, increment buffer pointer
2980                // and lop off page rollover to start at next page
2981                pTransmitBuffer += pGlobalData->fAlignQuadSize;
2982                pTransmitBuffer = (UInt32 *)((UInt32)pTransmitBuffer & 0xfffff000);
2983            }
2984
2985            pDCLTransferPacket->buffer = (UInt8 *) pTransmitBuffer;
2986            // increment by multiple of 16 to maintain cache alignment
2987            pTransmitBuffer += pGlobalData->fAlignQuadSize;
2988
2989            // Save first data packet DCL command.
2990            if (pFirstBufferGroupDCLCommand == NULL)
2991                pFirstBufferGroupDCLCommand = (DCLCommandPtr) pDCLCommand;
2992
2993            dataPacketNum++;
2994            numPackets++;
2995            emptyPacketNumerator += numEmptyPacketsInPlayBufferGroup;
2996
2997            if (emptyPacketNumerator >= pGlobalData->numDataPacketsPerGroup)
2998            {
2999                // Add skip jump if this is the first empty packet in the buffer group.
3000                if (pBufferGroupSkipEmptyPacketDCLLabel == NULL)
3001                {
3002                    pDCLJump = (DCLJumpPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLJump));
3003                    pPlayBufferGroupData->pBufferGroupSkipEmptyPacketDCLJump = pDCLJump;
3004                    pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLJump;
3005                    pDCLCommand = (DCLCommandPtr) pDCLJump;
3006                    pDCLJump->opcode = kDCLJumpOp | kFWDCLOpDynamicFlag;
3007
3008                    pDCLLabel = (DCLLabelPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLLabel));
3009                    pPlayBufferGroupData->pBufferGroupDontSkipEmptyPacketDCLLabel = pDCLLabel;
3010                    pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLLabel;
3011                    pDCLCommand = (DCLCommandPtr) pDCLLabel;
3012                    pDCLLabel->opcode = kDCLLabelOp;
3013                    pDCLJump->pJumpDCLLabel = pDCLLabel;
3014                }
3015
3016                // Send a packet.
3017                // Just CIP header.
3018                pDCLTransferPacket = (DCLTransferPacketPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLTransferPacket));
3019                pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLTransferPacket;
3020                pDCLCommand = (DCLCommandPtr) pDCLTransferPacket;
3021                pDCLTransferPacket->opcode = kDCLSendPacketStartOp;
3022                pDCLTransferPacket->buffer = pEmptyTransmitBuffer;
3023                pDCLTransferPacket->size = kDVPacketCIPSize;
3024
3025                // increment 16 bytes to maintain alignment
3026                pEmptyTransmitBuffer += kDVPacketCIPSize+kDVPacketAlignSlop;
3027                numPackets++;
3028                emptyPacketNumerator -= pGlobalData->numDataPacketsPerGroup;
3029
3030                // Add skip jump label if this is the first empty packet in the
3031                // buffer group.
3032                if (pBufferGroupSkipEmptyPacketDCLLabel == NULL)
3033                {
3034                    // Add skip label.
3035                    pDCLLabel = (DCLLabelPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLLabel));
3036                    pBufferGroupSkipEmptyPacketDCLLabel = pDCLLabel;
3037                    pPlayBufferGroupData->pBufferGroupSkipEmptyPacketDCLLabel = pBufferGroupSkipEmptyPacketDCLLabel;
3038                    pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLLabel;
3039                    pDCLCommand = (DCLCommandPtr) pDCLLabel;
3040                    pDCLLabel->opcode = kDCLLabelOp;
3041                }
3042            }
3043        }
3044         // Save number of packets in this buffer group, DCL update list size, and last
3045        // DCL command.
3046        pPlayBufferGroupData->numPackets = numPackets;
3047        pPlayBufferGroupData->pFirstBufferGroupDCLCommand = pFirstBufferGroupDCLCommand;
3048        pPlayBufferGroupData->pLastBufferGroupDCLCommand = (DCLCommandPtr) pDCLCommand;
3049
3050        // Create buffer group update list.
3051        DVCreatePlayBufferGroupUpdateList( pPlayBufferGroupData );
3052
3053        // Update total packet count.
3054        pGlobalData->totalPackets += numPackets;
3055        pGlobalData->activePackets += numPackets;
3056        // work out if we should start by skipping this empty packet.
3057
3058#if ALT_TIMING
3059	{
3060		UInt32 nominalProgramCycleTime = 0;
3061		UInt32 nominalActivePackets = 0;
3062		SInt32 cycleDrift = 0;
3063
3064		nominalProgramCycleTime = ConvertFractionalSecondsToFWCycleTime( pGlobalData->playFramePeriodNumerator*(kNumPlayBufferGroups/2),
3065																		 pGlobalData->playFramePeriodDenominator );
3066		nominalActivePackets = ((nominalProgramCycleTime & 0x01FFF000) >> 12);
3067		cycleDrift = pGlobalData->activePackets - ((nominalActivePackets*(bufferGroupNum+1)) / kNumPlayBufferGroups);
3068
3069//		syslog(LOG_INFO, "Group %d, active %d, nominal %d, cycleDrift %d\n", bufferGroupNum+1, pGlobalData->activePackets-numPackets, ((nominalActivePackets*(bufferGroupNum)) / kNumPlayBufferGroups), cycleDrift);
3070
3071		if(cycleDrift > 0) {
3072            pGlobalData->activePackets--;
3073            pPlayBufferGroupData->pBufferGroupSkipEmptyPacketDCLJump->pJumpDCLLabel = pPlayBufferGroupData->pBufferGroupSkipEmptyPacketDCLLabel;
3074            pPlayBufferGroupData->skippingEmptyPacket = true;
3075        }
3076        else
3077            pPlayBufferGroupData->skippingEmptyPacket = false;
3078	}
3079#else
3080		emptyError = getEmptyPacketsPerGroup(pGlobalData, pGlobalData->numDataPacketsPerGroup*(bufferGroupNum+1));
3081        emptyError = pGlobalData->activePackets - pGlobalData->numDataPacketsPerGroup*(bufferGroupNum+1) - emptyError;
3082
3083        //syslog(LOG_INFO, "Group %d, %d empty packets, Current error %d\n", bufferGroupNum, numEmptyPacketsInPlayBufferGroup, emptyError);
3084        if(emptyError > 0) {
3085            pGlobalData->activePackets--;
3086            pPlayBufferGroupData->pBufferGroupSkipEmptyPacketDCLJump->pJumpDCLLabel = pPlayBufferGroupData->pBufferGroupSkipEmptyPacketDCLLabel;
3087            pPlayBufferGroupData->skippingEmptyPacket = true;
3088        }
3089        else
3090            pPlayBufferGroupData->skippingEmptyPacket = false;
3091  #endif
3092
3093        // Get time stamp at end of buffer group.
3094        pDCLTimeStamp = (DCLPtrTimeStampPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLPtrTimeStamp));
3095        pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLTimeStamp;
3096        pDCLCommand = (DCLCommandPtr) pDCLTimeStamp;
3097        pDCLTimeStamp->opcode = kDCLPtrTimeStampOp;
3098        *pTimeStampPtr = 0xffffffff;	// Init to impossible time stamp
3099        pDCLTimeStamp->timeStampPtr = pTimeStampPtr++;
3100
3101        pPlayBufferGroupData->pBufferGroupTimeStampPtr = pDCLTimeStamp->timeStampPtr;
3102        pPlayBufferGroupData->timeStampUpdateDCLList = (DCLCommandPtr) pDCLTimeStamp;
3103
3104        // Create update DCL list to update time stamp.
3105        pDCLUpdateDCLList = (DCLUpdateDCLListPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLUpdateDCLList));
3106        pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLUpdateDCLList;
3107        pDCLCommand = (DCLCommandPtr) pDCLUpdateDCLList;
3108        pDCLUpdateDCLList->opcode = kDCLUpdateDCLListOp;
3109        pDCLUpdateDCLList->dclCommandList = &(pPlayBufferGroupData->timeStampUpdateDCLList);
3110        pDCLUpdateDCLList->numDCLCommands = 1;
3111
3112#if 0
3113		// TODO: Merge this update DCL list with the timestamp update list, above!!!!!
3114        // Create update DCL list to update buffers.
3115        pDCLUpdateDCLList = (DCLUpdateDCLListPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLUpdateDCLList));
3116        pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLUpdateDCLList;
3117        pDCLCommand = (DCLCommandPtr) pDCLUpdateDCLList;
3118        pDCLUpdateDCLList->opcode = kDCLUpdateDCLListOp;
3119        pDCLUpdateDCLList->dclCommandList = pPlayBufferGroupData->bufferGroupUpdateDCLList;
3120        pDCLUpdateDCLList->numDCLCommands = pPlayBufferGroupData->updateListSize;
3121#endif
3122
3123        // Create end of buffer group jump.
3124        pBufferGroupDCLJump = (DCLJumpPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLJump));
3125        pPlayBufferGroupData->pEndOfBufferGroupDCLJump = pBufferGroupDCLJump;
3126        pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pBufferGroupDCLJump;
3127        pDCLCommand = (DCLCommandPtr) pBufferGroupDCLJump;
3128        pBufferGroupDCLJump->opcode = kDCLJumpOp | kFWDCLOpDynamicFlag;
3129        pBufferGroupDCLJump->pJumpDCLLabel = nil; // For now, this will be updated later!
3130    }
3131
3132    // Create label for underrun.
3133    pUnderrunDCLLabel = (DCLLabelPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLLabel));
3134    pGlobalData->pUnderrunDCLLabel = pUnderrunDCLLabel;
3135    pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pUnderrunDCLLabel;
3136    pDCLCommand = (DCLCommandPtr) pUnderrunDCLLabel;
3137    pUnderrunDCLLabel->opcode = kDCLLabelOp;
3138
3139	// Send a garbage packet (just CIP only!). Required for a valid DCL program!
3140	pDCLTransferPacket = (DCLTransferPacketPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLTransferPacket));
3141	pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLTransferPacket;
3142	pDCLCommand = (DCLCommandPtr) pDCLTransferPacket;
3143	pDCLTransferPacket->opcode = kDCLSendPacketStartOp;
3144	pDCLTransferPacket->buffer = pGlobalData->pEmptyTransmitBuffers;
3145	pDCLTransferPacket->size = kDVPacketCIPSize;
3146
3147    // Call underrun proc.
3148    // This is the last command.
3149    pDCLCallProc = (DCLCallProcPtr) AllocateDCLCommand (pGlobalData, sizeof (DCLCallProc));
3150    pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLCallProc;
3151    pDCLCallProc->pNextDCLCommand = NULL;
3152    pDCLCallProc->opcode = kDCLCallProcOp;
3153    pDCLCallProc->proc = DVHandleOutputUnderrun;
3154    pDCLCallProc->procData = (UInt32) pGlobalData;
3155
3156	// Fix up jump targets
3157    for (bufferGroupNum = 0; bufferGroupNum < kNumPlayBufferGroups; bufferGroupNum++)
3158    {
3159		pPlayBufferGroupData = &pGlobalData->fLocalDataArray[bufferGroupNum];
3160
3161		if (bufferGroupNum == (kNumPlayBufferGroups-1))
3162			 pPlayBufferGroupData->pEndOfBufferGroupDCLJump->pJumpDCLLabel =
3163				 pGlobalData->pUnderrunDCLLabel;
3164		else
3165			 pPlayBufferGroupData->pEndOfBufferGroupDCLJump->pJumpDCLLabel =
3166				pGlobalData->fLocalDataArray[bufferGroupNum+1].pStartOfBufferGroupDCLLabel;
3167	}
3168
3169#ifdef kIDH_Verbose_Debug_Logging
3170	syslog(LOG_INFO, "DVLib: buildWriteProgram end\n");
3171#endif
3172    return kIOReturnSuccess;
3173
3174bail:
3175
3176#ifdef kIDH_Verbose_Debug_Logging
3177		syslog(LOG_INFO, "DVLib: buildWriteProgram end:fail\n");
3178#endif
3179
3180    return res;
3181}
3182
3183DVGlobalOutPtr DVAllocWrite(DVDevice *device, DVThread *thread)
3184{
3185    DVGlobalOutPtr globs;
3186
3187#ifdef kIDH_Verbose_Debug_Logging
3188	syslog(LOG_INFO, "DVLib: DVAllocWrite begin\n");
3189#endif
3190
3191    globs = malloc(sizeof(DVGlobalOut));
3192    if(!globs)
3193        return NULL;
3194    bzero(globs, sizeof(DVGlobalOut));
3195    initStream(&globs->fStreamVars, device, device->fOutPlug, device->fWriteChan, thread);
3196    globs->fUpdateBuffers = 1;
3197
3198#ifdef kIDH_Verbose_Debug_Logging
3199	syslog(LOG_INFO, "DVLib: DVAllocWrite end\n");
3200#endif
3201
3202    return globs;
3203}
3204
3205IOReturn DVWriteSetSignalMode(DVGlobalOutPtr globs, UInt8 mode)
3206{
3207    globs->fStreamVars.fSignalMode = mode;
3208
3209#ifdef kIDH_Verbose_Debug_Logging
3210	syslog(LOG_INFO, "DVLib: DVWriteSetSignalMode begin\n");
3211#endif
3212
3213	switch (mode)
3214	{
3215		// NTSC SD or DVCPro25
3216		case kAVCSignalModeSD525_60:
3217		case kAVCSignalModeDVCPro525_60:
3218			globs->fStreamVars.fDVFrameSize = kFrameSize_SD525_60;
3219			break;
3220
3221		// PAL SD or DVCPro25
3222		case kAVCSignalModeSD625_50:
3223		case kAVCSignalModeDVCPro625_50:
3224			globs->fStreamVars.fDVFrameSize = kFrameSize_SD625_50;
3225			break;
3226
3227		// NTSC SDL
3228		case kAVCSignalModeSDL525_60:
3229			globs->fStreamVars.fDVFrameSize = kFrameSize_SDL525_60;
3230			break;
3231
3232		// PAL SDL
3233		case kAVCSignalModeSDL625_50:
3234			globs->fStreamVars.fDVFrameSize = kFrameSize_SDL625_50;
3235			break;
3236
3237		// NTSC DVCPro50 or HD
3238		case kAVCSignalModeDVCPro50_525_60:
3239		case kAVCSignalModeHD1125_60:
3240			globs->fStreamVars.fDVFrameSize = kFrameSize_DVCPro50_525_60;
3241			break;
3242
3243		// PAL DVCPro50 or HD
3244		case kAVCSignalModeDVCPro50_625_50:
3245		case kAVCSignalModeHD1250_50:
3246			globs->fStreamVars.fDVFrameSize = kFrameSize_DVCPro50_625_50;
3247			break;
3248
3249		// NTSC DVCPro100
3250		case kAVCSignalModeDVCPro100_525_60:
3251			globs->fStreamVars.fDVFrameSize = kFrameSize_DVCPro100_525_60;
3252			break;
3253
3254		// PAL DVCPro100
3255		case kAVCSignalModeDVCPro100_625_50:
3256			globs->fStreamVars.fDVFrameSize = kFrameSize_DVCPro100_625_50;
3257			break;
3258
3259		default:
3260			globs->fStreamVars.fDVFrameSize = kFrameSize_SD625_50;
3261			break;
3262	};
3263
3264#ifdef kIDH_Verbose_Debug_Logging
3265	syslog(LOG_INFO, "DVLib: DVWriteSetSignalMode end\n");
3266#endif
3267
3268    return kIOReturnSuccess;
3269}
3270
3271IOReturn DVWriteAllocFrames(DVGlobalOutPtr pGlobalData, UInt32 numFrames,
3272    DVFrameVars **frameVars, UInt8 **frames)
3273{
3274    IOReturn err;
3275
3276#ifdef kIDH_Verbose_Debug_Logging
3277	syslog(LOG_INFO, "DVLib: DVWriteAllocFrames begin\n");
3278#endif
3279
3280   do {
3281        err = DVAllocFrames(&pGlobalData->fStreamVars.fFrames,
3282							numFrames,
3283							pGlobalData->fStreamVars.fDVFrameSize,
3284							frameVars,
3285							frames);
3286        if(err != kIOReturnSuccess)
3287            break;
3288        err = allocateBuffers(pGlobalData);
3289         if(err != kIOReturnSuccess)
3290            break;
3291
3292        err = buildWriteProgram(pGlobalData);
3293    } while (0);
3294
3295#ifdef kIDH_Verbose_Debug_Logging
3296   syslog(LOG_INFO, "DVLib: DVWriteAllocFrames end\n");
3297#endif
3298
3299   return err;
3300}
3301
3302UInt8 * DVWriteGetDCLBuffer(DVGlobalOutPtr pGlobalData, DVSharedVars **varPtr)
3303{
3304    // Caller is now responsible for filling up the DCL buffers
3305    pGlobalData->fUpdateBuffers = 0;
3306    *varPtr = &pGlobalData->fSharedDCLVars;
3307    return pGlobalData->fStreamVars.fDCLBuffers;
3308}
3309
3310static IOReturn doDVWriteStart(DVGlobalOutPtr pGlobalData)
3311{
3312    IOReturn err;
3313    DVLocalOutPtr	pPlayBufferGroupData;
3314    UInt32			bufferGroupNum;
3315    int i;
3316    DVThread *		dvThread = pGlobalData->fStreamVars.fThread;
3317
3318#ifdef kIDH_Verbose_Debug_Logging
3319	syslog(LOG_INFO, "DVLib: doDVWriteStart begin\n");
3320#endif
3321
3322    do {
3323
3324        // Set up all of the buffer groups.
3325        //syslog(LOG_INFO, "DVWrite: Setup all of the buffer groups\n");
3326
3327        pGlobalData->nextSYT = kPlaySYTDelay<<12;
3328        pGlobalData->nextDBC = 0;
3329        pGlobalData->nextDataPacketNum = 0;
3330        pGlobalData->pImageBuffer = NULL;
3331        pGlobalData->fSharedDCLVars.fDMAPos = 0;
3332
3333        pGlobalData->pendingDVWriteUnderrunHandler = false;
3334        pGlobalData->deferredDVWriteFree = false;
3335        pGlobalData->dvWriteStopInProgress = false;
3336
3337        pPlayBufferGroupData = &pGlobalData->fLocalDataArray[0];
3338        for (bufferGroupNum = 0; bufferGroupNum < kNumPlayBufferGroups; bufferGroupNum++)
3339        {
3340            DVUpdateOutputBuffers( pPlayBufferGroupData);
3341            pPlayBufferGroupData = pPlayBufferGroupData->pNextLocalData;
3342        }
3343        err = openStream(&pGlobalData->fStreamVars, true, pGlobalData->fDataPacketSize + kDVPacketCIPSize);
3344
3345        for(i=0; i<kDVMaxStreamsActive; i++) {
3346            if(dvThread->fOutStreams[i] == NULL) {
3347                dvThread->fOutStreams[i] = pGlobalData;
3348                break;
3349            }
3350            else if(dvThread->fOutStreams[i] == pGlobalData) {
3351                break;	// Happens when restarting.
3352            }
3353        }
3354
3355    } while (0);
3356    //syslog(LOG_INFO, "doDVWriteStart exit, err %x\n", err);
3357
3358#ifdef kIDH_Verbose_Debug_Logging
3359	syslog(LOG_INFO, "DVLib: doDVWriteStart end\n");
3360#endif
3361
3362	return err;
3363}
3364
3365IOReturn DVWriteStart(DVGlobalOutPtr pGlobalData)
3366{
3367    IOReturn err;
3368
3369#ifdef kIDH_Verbose_Debug_Logging
3370	syslog(LOG_INFO, "DVLib: DVWriteStart begin\n");
3371#endif
3372
3373    err = DVRequest(pGlobalData->fStreamVars.fThread, doDVWriteStart, pGlobalData, 0);
3374
3375#ifdef kIDH_Verbose_Debug_Logging
3376	syslog(LOG_INFO, "DVLib: DVWriteStart end\n");
3377#endif
3378
3379	return err;
3380}
3381
3382static void doDVWriteStop(DVGlobalOutPtr pGlobalData)
3383{
3384    int i;
3385
3386#ifdef kIDH_Verbose_Debug_Logging
3387	syslog(LOG_INFO, "DVLib: doDVWriteStop begin\n");
3388#endif
3389
3390    pGlobalData->dvWriteStopInProgress = true;
3391
3392    DVThread *		dvThread = pGlobalData->fStreamVars.fThread;
3393
3394    for(i=0; i<kDVMaxStreamsActive; i++) {
3395        if(dvThread->fOutStreams[i] == pGlobalData) {
3396            dvThread->fOutStreams[i] = NULL;
3397            break;
3398        }
3399    }
3400    closeStream(&pGlobalData->fStreamVars);
3401
3402    BreakP2PConnectionForWrite(pGlobalData->fStreamVars.pDVDevice,0,pGlobalData->fStreamVars.fIsocChannel);
3403
3404    DVDisposeDCLOutput(pGlobalData);
3405
3406    pGlobalData->dvWriteStopInProgress = false;
3407
3408#ifdef kIDH_Verbose_Debug_Logging
3409	syslog(LOG_INFO, "DVLib: doDVWriteStop end\n");
3410#endif
3411
3412}
3413
3414void DVWriteStop(DVGlobalOutPtr pGlobalData)
3415{
3416#ifdef kIDH_Verbose_Debug_Logging
3417	syslog(LOG_INFO, "DVLib: DVWriteStop begin\n");
3418#endif
3419
3420    DVRequest(pGlobalData->fStreamVars.fThread, doDVWriteStop, pGlobalData, 0);
3421
3422#ifdef kIDH_Verbose_Debug_Logging
3423	syslog(LOG_INFO, "DVLib: DVWriteStop end\n");
3424#endif
3425
3426}
3427
3428void DVWriteFreeFrames(DVGlobalOutPtr globs)
3429{
3430
3431#ifdef kIDH_Verbose_Debug_Logging
3432	syslog(LOG_INFO, "DVLib: DVWriteFreeFrames begin\n");
3433#endif
3434
3435    DVFreeFrames(&globs->fStreamVars.fFrames);
3436
3437#ifdef kIDH_Verbose_Debug_Logging
3438	syslog(LOG_INFO, "DVLib: DVWriteFreeFrames end\n");
3439#endif
3440
3441}
3442
3443void DVWriteFree(DVGlobalOutPtr globs)
3444{
3445
3446#ifdef kIDH_Verbose_Debug_Logging
3447	syslog(LOG_INFO, "DVLib: DVWriteFree begin\n");
3448#endif
3449
3450    if (globs->pendingDVWriteUnderrunHandler == true)
3451	globs->deferredDVWriteFree = true;
3452    else
3453	free(globs);
3454
3455#ifdef kIDH_Verbose_Debug_Logging
3456	syslog(LOG_INFO, "DVLib: DVWriteFree end\n");
3457#endif
3458
3459}
3460
3461DVGlobalInPtr DVAllocRead(DVDevice *device, DVThread *thread)
3462{
3463    DVGlobalInPtr globs;
3464
3465#ifdef kIDH_Verbose_Debug_Logging
3466	syslog(LOG_INFO, "DVLib: DVAllocRead begin\n");
3467#endif
3468
3469    globs = malloc(sizeof(DVGlobalIn));
3470    if(!globs)
3471        return NULL;
3472    bzero(globs, sizeof(DVGlobalIn));
3473    initStream(&globs->fStreamVars, device, kNoPlug, device->fReadChan, thread);
3474
3475	// Set the initial read signal mode for standard DV.
3476	// This can be overriden later by a call to DVReadSetSignalMode
3477	if (device->standard == 0)
3478		DVReadSetSignalMode(globs,0x00);	// NTSC-DV
3479	else
3480		DVReadSetSignalMode(globs,0x80);	// PAL-DV
3481
3482    return globs;
3483
3484#ifdef kIDH_Verbose_Debug_Logging
3485	syslog(LOG_INFO, "DVLib: DVAllocRead end\n");
3486#endif
3487
3488}
3489
3490IOReturn DVReadSetSignalMode(DVGlobalInPtr globs, UInt8 mode)
3491{
3492
3493#ifdef kIDH_Verbose_Debug_Logging
3494	syslog(LOG_INFO, "DVLib: DVReadSetSignalMode begin\n");
3495#endif
3496
3497    globs->fStreamVars.fSignalMode = mode;
3498
3499    switch (mode)
3500	{
3501		// NTSC SD or DVCPro25
3502		case kAVCSignalModeSD525_60:
3503		case kAVCSignalModeDVCPro525_60:
3504			globs->fStreamVars.fDVFrameSize = kFrameSize_SD525_60;
3505			break;
3506
3507		// PAL SD or DVCPro25
3508		case kAVCSignalModeSD625_50:
3509		case kAVCSignalModeDVCPro625_50:
3510			globs->fStreamVars.fDVFrameSize = kFrameSize_SD625_50;
3511			break;
3512
3513		// NTSC SDL
3514		case kAVCSignalModeSDL525_60:
3515			// override SDL modes to SD
3516			globs->fStreamVars.fSignalMode = kAVCSignalModeSD525_60;
3517			globs->fStreamVars.fDVFrameSize = kFrameSize_SD525_60;
3518			break;
3519
3520		// PAL SDL
3521		case kAVCSignalModeSDL625_50:
3522			// override SDL modes to SD
3523			globs->fStreamVars.fSignalMode = kAVCSignalModeSD625_50;
3524			globs->fStreamVars.fDVFrameSize = kFrameSize_SD625_50;
3525			break;
3526
3527		// NTSC DVCPro50 or HD
3528		case kAVCSignalModeDVCPro50_525_60:
3529		case kAVCSignalModeHD1125_60:
3530			globs->fStreamVars.fDVFrameSize = kFrameSize_DVCPro50_525_60;
3531			break;
3532
3533		// PAL DVCPro50 or HD
3534		case kAVCSignalModeDVCPro50_625_50:
3535		case kAVCSignalModeHD1250_50:
3536			globs->fStreamVars.fDVFrameSize = kFrameSize_DVCPro50_625_50;
3537			break;
3538
3539		// NTSC DVCPro100
3540		case kAVCSignalModeDVCPro100_525_60:
3541			globs->fStreamVars.fDVFrameSize = kFrameSize_DVCPro100_525_60;
3542			break;
3543
3544		// PAL DVCPro100
3545		case kAVCSignalModeDVCPro100_625_50:
3546			globs->fStreamVars.fDVFrameSize = kFrameSize_DVCPro100_625_50;
3547			break;
3548
3549		default:
3550			// override the specified mode if it's not one of our supported modes.
3551			globs->fStreamVars.fSignalMode = kAVCSignalModeSD625_50;
3552			globs->fStreamVars.fDVFrameSize = kFrameSize_SD625_50;
3553			break;
3554	};
3555
3556
3557#ifdef kIDH_Verbose_Debug_Logging
3558	syslog(LOG_INFO, "DVLib: DVReadSetSignalMode end\n");
3559#endif
3560
3561	return kIOReturnSuccess;
3562}
3563
3564IOReturn DVReadAllocFrames(DVGlobalInPtr globs, UInt32 numFrames,
3565    DVFrameVars **frameVars, UInt8 **frames)
3566{
3567	IOReturn err;
3568
3569#ifdef kIDH_Verbose_Debug_Logging
3570	syslog(LOG_INFO, "DVLib: DVReadAllocFrames begin\n");
3571#endif
3572
3573    err = DVAllocFrames(&globs->fStreamVars.fFrames,
3574							numFrames,
3575							globs->fStreamVars.fDVFrameSize,
3576							frameVars,
3577							frames);
3578
3579#ifdef kIDH_Verbose_Debug_Logging
3580	syslog(LOG_INFO, "DVLib: DVReadAllocFrames end\n");
3581#endif
3582
3583	return err;
3584}
3585
3586static void doDVReadHandleInputUnderrun( DVGlobalInPtr pGlobalData )
3587{
3588	UInt32 timeNow, lastFrameTime;
3589    DVStream *stream;
3590	int	pingPongNum;
3591
3592
3593    if ((pGlobalData->pendingDVReadUnderrunHandler == true) && (pGlobalData->deferredDVReadFree == true))
3594    {
3595	// Free the globalin data struct
3596	free(pGlobalData);
3597	return;
3598    }
3599    pGlobalData->pendingDVReadUnderrunHandler = false;
3600
3601    stream = &pGlobalData->fStreamVars;
3602
3603	// See if stream still open. If not, we're done!
3604	if (stream->fIsochChannelRef == NULL)
3605		return;
3606
3607    (*stream->pFWDevice)->
3608        GetCycleTime(stream->pFWDevice, &timeNow);
3609    syslog(LOG_INFO, "At %8.3f Req time %8.3f, now %8.3f\n",
3610        stream->fThread->setTimeoutTime, stream->fThread->requestTimeoutTime, CFAbsoluteTimeGetCurrent());
3611    syslog(LOG_INFO, "DVReadHandleInputUnderrun: 0x%p, last block = %d, status %d, writer %d reader %d timeNow %x\n",
3612        pGlobalData, pGlobalData->fState, stream->fFrames.fStatus,
3613        stream->fFrames.fWriter, stream->fFrames.fReader, timeNow);
3614
3615    lastFrameTime = stream->fFrames.fFrameTime[(stream->fFrames.fWriter-1)%stream->fFrames.fNumFrames];
3616
3617	// Stop
3618	(*stream->fIsochChannelRef)->Stop(stream->fIsochChannelRef);
3619
3620	// Fixup DCL program jumps and timestamps
3621	for (pingPongNum = 0; pingPongNum < kNumPingPongs; pingPongNum++)
3622    {
3623		if (pingPongNum < (kNumPingPongs-1))
3624			ModifyDCLJump(pGlobalData->fStreamVars.pFWLocalIsochPort,
3625				pGlobalData->fLocalDataArray[pingPongNum].fStateJmp, pGlobalData->fLocalDataArray[pingPongNum+1].fStateLabel);
3626		else
3627			ModifyDCLJump(pGlobalData->fStreamVars.pFWLocalIsochPort,
3628				 pGlobalData->fLocalDataArray[pingPongNum].fStateJmp, pGlobalData->fTerminal);
3629
3630		*pGlobalData->fLocalDataArray[pingPongNum].fTimeStampPtr = 0xffffffff;
3631	}
3632
3633	// Reset some vars
3634	pGlobalData->fState = 0;
3635    pGlobalData->fSynced = 0;
3636    pGlobalData->fRestarted = true;
3637    pGlobalData->fLastFrameTime = lastFrameTime;
3638
3639	// Restart
3640	(*stream->fIsochChannelRef)->Start(stream->fIsochChannelRef);
3641}
3642
3643static void DVReadHandleInputUnderrun( DCLCommandPtr pDCLCommandPtr )
3644{
3645    DVGlobalInPtr pGlobalData;
3646
3647#ifdef kIDH_Verbose_Debug_Logging
3648	syslog(LOG_INFO, "DVLib: DVReadHandleInputUnderrun begin\n");
3649#endif
3650
3651    pGlobalData = (DVGlobalInPtr)((DCLCallProcPtr)pDCLCommandPtr)->procData;
3652    if (pGlobalData->dvReadStopInProgress == false)
3653    {
3654        pGlobalData->pendingDVReadUnderrunHandler = true;
3655        DVRequest(pGlobalData->fStreamVars.fThread, doDVReadHandleInputUnderrun, pGlobalData, 0);
3656    }
3657
3658#ifdef kIDH_Verbose_Debug_Logging
3659	syslog(LOG_INFO, "DVLib: DVReadHandleInputUnderrun end\n");
3660#endif
3661
3662}
3663
3664static void DVStorePackets(DVLocalInPtr pLocalData)
3665{
3666    DVGlobalInPtr pGlobalData;
3667    DCLCommandPtr pCurrentCmd;
3668    DCLTransferPacketPtr pDCLTransferPacket;
3669    UInt8 * pPacketBuffer;
3670    UInt32 packetHeader, packetSize, packetNum, packetPerFrame;
3671    bool vSyncDetected;
3672    UInt8 currentSequenceCount;
3673    int prevBlock;
3674	UInt8 fn;
3675	UInt8 stype;
3676	UInt32 actualModeFrameSize;
3677	short syncData;
3678	UInt32 cipHeader;
3679
3680#if TIMING
3681    CFAbsoluteTime cstart, cend;
3682    cstart = CFAbsoluteTimeGetCurrent();
3683#endif
3684
3685    pGlobalData = pLocalData->pGlobalData;
3686
3687    //printf("Timestamp %p = %x\n", pLocalData->fTimeStampPtr, *pLocalData->fTimeStampPtr);
3688
3689    // Get info from ping pong data.
3690    pCurrentCmd = pLocalData->fFirstCmd;
3691
3692    // How many packets we talkin'?
3693    packetPerFrame = (pGlobalData->fStreamVars.fSignalMode & kAVCSignalModeMask_50) ?
3694                            kPALNumDataPacketsPerDVFrame : kNTSCNumDataPacketsPerDVFrame;
3695    for ( packetNum = 0; packetNum < kNumPacketsPerInputBuffer; packetNum++ )
3696    {
3697        // compute size of transfer
3698        pDCLTransferPacket = (DCLTransferPacketPtr) pCurrentCmd;
3699        pPacketBuffer = (UInt8*)pDCLTransferPacket->buffer;
3700        packetHeader = *((UInt32*) pPacketBuffer);
3701        pPacketBuffer += 4; // 4 byte 1394 header
3702        packetSize = (packetHeader & kFWIsochDataLength) >> kFWIsochDataLengthPhase;
3703#if 1
3704		// Calculate fn
3705		fn = ((pPacketBuffer[2] & 0xC0) >> 6);
3706		if (fn == 0)
3707			fn = 1;
3708		else
3709			fn = 1 << fn;
3710
3711        // Check for corrupt packets, otherwise we may die horribly later in bcopy()
3712        if(packetSize < 8) {
3713            syslog(LOG_INFO, "DVStorePackets: size %d header 0x%x\n", packetSize, packetHeader);
3714            packetSize = 8;
3715        }
3716        else
3717		{
3718			// Check for packet size not matching CIP header specified packet size
3719			if(packetSize > 8 && packetSize != (pPacketBuffer[1]*4*fn) + 8) {
3720				syslog(LOG_INFO, "DVStorePackets: size %d header 0x%x\n", packetSize, packetHeader);
3721				packetSize = 8;
3722			}
3723
3724			// Check to make sure the signal mode in the CIP header is what we're expecting
3725			cipHeader = *(UInt32 *)(pPacketBuffer+4);
3726			cipHeader = EndianU32_BtoN( cipHeader );
3727			if (pGlobalData->fStreamVars.fSignalMode != ((cipHeader >> 16) & 0xff))
3728			{
3729				// CIP DV-mode doesn't match the configured mode! To prevent a crash, we
3730				// should only store packets if the CIP DV-mode frame-size will
3731				// fit into our allocated frame-buffers!
3732
3733				if (((cipHeader >> 16) & 0xff) & kAVCSignalModeMask_50)
3734					actualModeFrameSize = kPALNumDataPacketsPerDVFrame * (packetSize-8);
3735				else
3736					actualModeFrameSize = kNTSCNumDataPacketsPerDVFrame * (packetSize-8);
3737
3738				if (actualModeFrameSize >  pGlobalData->fStreamVars.fDVFrameSize)
3739				{
3740					syslog(LOG_INFO, "DVStorePackets (received frame too large for frame-buffer): expected DV mode: %d, actual DV mode: %d\n",
3741						pGlobalData->fStreamVars.fSignalMode, (cipHeader >> 16) & 0xff);
3742					packetSize = 8;
3743				}
3744			}
3745        }
3746#endif
3747        // skip empty packets
3748        if( packetSize > 8 ) {
3749            // get current data block sequence counter value and increment saved value
3750            currentSequenceCount = pPacketBuffer[3];
3751
3752			// Increment lastSequenceCount based on stream type
3753			// TODO: This will need to change to support 2x,4x modes on some signal types
3754			stype = pGlobalData->fStreamVars.fSignalMode & kAVCSignalModeMask_STYPE;
3755			switch (stype)
3756			{
3757				case kAVCSignalModeMask_DVCPro50:
3758					pGlobalData->lastSequenceCount += 2;
3759					break;
3760
3761				case kAVCSignalModeMask_DVCPro100:
3762					pGlobalData->lastSequenceCount += 4;
3763					break;
3764
3765				case kAVCSignalModeMask_SDL:
3766				case kAVCSignalModeMask_DVCPro25:
3767				case kAVCSignalModeMask_HD:
3768				default:	// SD video stream
3769					pGlobalData->lastSequenceCount += 1;
3770					break;
3771			};
3772
3773            // Want size minus CIP header
3774            packetSize -= 8;
3775            // detect vSync
3776
3777           syncData = *(short *)(pPacketBuffer + 8);			  syncData = EndianS16_BtoN(syncData);
3778
3779		  vSyncDetected = ((syncData & 0xE0F8 ) == 0x0000 );
3780            if( vSyncDetected ) {
3781                // Calculate when Sync arrived.
3782                UInt32 frameEnd = SubtractFWCycleTimeFromFWCycleTime(*pLocalData->fTimeStampPtr, (kNumPacketsPerInputBuffer - packetNum) << 12);
3783                UInt32 cip2 = *(UInt32 *)(pPacketBuffer+4);
3784				cip2 = EndianU32_BtoN( cip2 );
3785                pGlobalData->fStreamVars.fSignalMode = (cip2 >> 16) & 0xff;
3786                packetPerFrame = (pGlobalData->fStreamVars.fSignalMode & kAVCSignalModeMask_50) ?
3787                                        kPALNumDataPacketsPerDVFrame : kNTSCNumDataPacketsPerDVFrame;
3788                // if we got our frameSync at the right time
3789                if( pGlobalData->packetCount == packetPerFrame ) {
3790                    //printf("Frame received @ %x (w%d r%d)\n", frameEnd, pGlobalData->fStreamVars.fFrames.fWriter, pGlobalData->fStreamVars.fFrames.fReader);
3791
3792                    // Set amount read
3793                    DVSetInputFrameSizeAndMode(&pGlobalData->fStreamVars.fFrames, packetPerFrame * packetSize,
3794                                                                    pGlobalData->fStreamVars.fSignalMode, frameEnd);
3795                }
3796                else {
3797                    //printf("Sync detected @ %x\n", frameEnd);
3798                    if(pGlobalData->fRestarted) {
3799                        // Calculate how many frames missed.
3800                        // Fiddly mess from OHCI, have 16 bits from the middle of the cycle register (3 second bits + 13 cyle bits)
3801                        UInt32 lastFrameTime;
3802                        SInt32 cycleDiff, secsDiff;
3803                        UInt32 dropped;
3804
3805                        lastFrameTime = pGlobalData->fLastFrameTime;
3806                        cycleDiff = ((frameEnd & 0x01FFF000) - (lastFrameTime & 0x01FFF000));
3807                        if(cycleDiff < 0) {
3808                            cycleDiff += 8000 << 12;
3809                            frameEnd -= 0x02000000;
3810                        }
3811                        secsDiff = (frameEnd & 0x0e000000) - (lastFrameTime & 0x0e000000);
3812                        if(secsDiff < 0)
3813                            secsDiff += 0x10000000;
3814                        secsDiff >>= 25;
3815                        cycleDiff >>= 12;
3816                        cycleDiff += secsDiff * 8000;
3817                        if(pGlobalData->fStreamVars.fSignalMode & kAVCSignalModeMask_50)
3818                            dropped = (cycleDiff * kPALFrameRateNumerator + (4000*kPALFrameRateDenominator)) / (8000*kPALFrameRateDenominator);
3819                        else
3820                            dropped = (cycleDiff * kNTSCFrameRateNumerator + (4000*kNTSCFrameRateDenominator)) / (8000*kNTSCFrameRateDenominator);
3821                        //printf("At sync, dropped %d frames\n", dropped);
3822                        pGlobalData->fStreamVars.fFrames.fDroppedFrames += dropped;
3823                        pGlobalData->fRestarted = false;
3824                    }
3825                }
3826
3827                // start a new frame
3828                pGlobalData->packetCount = 0;
3829                pGlobalData->lastSequenceCount = currentSequenceCount;
3830                DVGetNextEmptyInputFrame(&pGlobalData->fStreamVars.fFrames,
3831											&(pGlobalData->pImageBuffer),
3832											pGlobalData->fStreamVars.fDVFrameSize);
3833                //printf("Filling frame %p (w%d r%d)\n",
3834                //    pGlobalData->pImageBuffer, pGlobalData->fStreamVars.fFrames.fWriter, pGlobalData->fStreamVars.fFrames.fReader);
3835
3836                pGlobalData->fSynced = true;
3837            }
3838
3839            if(pGlobalData->fSynced) {
3840                // skip over CIP header
3841                pPacketBuffer += 8;  // 8 bytes
3842
3843                if (currentSequenceCount == pGlobalData->lastSequenceCount && pGlobalData->packetCount < packetPerFrame) {
3844                    // store the packet
3845                    bcopy( pPacketBuffer, (void *)((UInt32) pGlobalData->pImageBuffer + (pGlobalData->packetCount * packetSize)), packetSize );
3846                    pGlobalData->packetCount++;
3847                }
3848                else {
3849                    // IOLog("DVStorePacket: dropped frame: packet out of sequence\n");
3850                    // packet out of sequence or too many packets between vSync detection, start new frame
3851                    //printf("Lost sync: %d-%d; %d-%d\n",
3852                    //    currentSequenceCount, pGlobalData->lastSequenceCount, pGlobalData->packetCount, packetPerFrame);
3853                    pGlobalData->packetCount = 0;
3854                    pGlobalData->fSynced = false;
3855                }
3856            }
3857            // Set last count to current count to resynch counts if bad sequence
3858            pGlobalData->lastSequenceCount = currentSequenceCount;
3859        }
3860        // update for next packet
3861        pCurrentCmd = pCurrentCmd->pNextDCLCommand;
3862    }
3863
3864
3865    // This block is ready for reuse, link previous block to this, this one to terminator
3866
3867    pGlobalData->fState = pLocalData->fBlockNum;
3868
3869    if(pLocalData->fBlockNum == 0)
3870        prevBlock = kNumPingPongs-1;
3871    else
3872        prevBlock = pLocalData->fBlockNum-1;
3873
3874    ModifyDCLJump(pGlobalData->fStreamVars.pFWLocalIsochPort,
3875            pGlobalData->fLocalDataArray[prevBlock].fStateJmp, pLocalData->fStateLabel);
3876    ModifyDCLJump(pGlobalData->fStreamVars.pFWLocalIsochPort, pLocalData->fStateJmp, pGlobalData->fTerminal);
3877
3878#if TIMING
3879    cend = CFAbsoluteTimeGetCurrent();
3880    DVLog(pGlobalData->fStreamVars.fThread, 'isoc', cstart, cend);
3881#endif
3882}
3883
3884void DVReadPoll(DVGlobalInPtr globs)
3885{
3886    int i, pos;
3887    pos = globs->fState;
3888    for(i=pos; i<kNumPingPongs; i++)
3889        if(*globs->fLocalDataArray[i].fTimeStampPtr != 0xffffffff) {
3890            DVStorePackets(&globs->fLocalDataArray[i]);
3891            *globs->fLocalDataArray[i].fTimeStampPtr = 0xffffffff;
3892        }
3893
3894    for(i=0; i<pos; i++)
3895        if(*globs->fLocalDataArray[i].fTimeStampPtr != 0xffffffff) {
3896            DVStorePackets(&globs->fLocalDataArray[i]);
3897            *globs->fLocalDataArray[i].fTimeStampPtr = 0xffffffff;
3898        }
3899}
3900
3901IOReturn DVReadStart(DVGlobalInPtr globs)
3902{
3903    DCLCommandPtr	opcodes;
3904    UInt8 *			pingPongBuffer = NULL;
3905    UInt8 *			pingPongPtr;
3906    UInt8 *			pDCLCommand;
3907    DCLLabelPtr			pStartDCLLabel;
3908    DCLLabelPtr			pBlockDCLLabel;
3909    DCLLabelPtr			pUnderrunDCLLabel;
3910    DCLTransferPacketPtr	pDCLTransferPacket;
3911    DCLPtrTimeStampPtr	pDCLTimeStamp;
3912//    DCLCallProcPtr		pDCLPingPongProc;
3913    DCLCallProcPtr      pUnderrunDCLCallProc;
3914    DCLJumpPtr			pDCLPingPongLoop;
3915    int				pingPongNum, packetNum;
3916    UInt32			updateListSize;
3917    UInt32			bufferSize;
3918    DCLUpdateDCLListPtr		pDCLUpdateDCLList;
3919    DCLCommandPtr		*updateDCLList, *startUpdateDCLList;
3920    DVLocalInPtr		pLocalData;
3921    IOReturn			res;
3922    UInt32 *			timeStampPtr;
3923    int 				i;
3924	UInt32				packetBufferSize;
3925	UInt32 				alignedDVPacketSize;
3926	UInt32 				pingPongBufferSize;
3927
3928#ifdef kIDH_Verbose_Debug_Logging
3929	syslog(LOG_INFO, "DVLib: DVReadStart begin\n");
3930#endif
3931
3932    //syslog(LOG_INFO, "DVReadStart() %p\n", globs);
3933
3934    // init variables
3935    pingPongBuffer = NULL;
3936
3937    globs->fStreamVars.pDCLList = NULL;
3938    globs->ppUpdateDCLList = NULL;
3939    globs->pImageBuffer = NULL;
3940    globs->fStreamVars.fDCLBuffers = NULL;
3941//    globs->fStreamVars.fSignalMode = kAVCSignalModeMask_50;	// initialize to bigger packets per frame (PAL)
3942    globs->packetCount = 0;
3943    globs->fState = 0;
3944
3945    globs->pendingDVReadUnderrunHandler = false;
3946    globs->deferredDVReadFree = false;
3947    globs->dvReadStopInProgress = false;
3948
3949	switch (globs->fStreamVars.fSignalMode)
3950	{
3951
3952		case kAVCSignalModeSDL525_60:
3953		case kAVCSignalModeSDL625_50:
3954			packetBufferSize = 252;
3955			alignedDVPacketSize = 512;
3956			break;
3957
3958		case kAVCSignalModeDVCPro50_525_60:
3959		case kAVCSignalModeHD1125_60:
3960		case kAVCSignalModeDVCPro50_625_50:
3961		case kAVCSignalModeHD1250_50:
3962			packetBufferSize = 972;
3963			alignedDVPacketSize = 1024;
3964			break;
3965
3966		case kAVCSignalModeDVCPro100_525_60:
3967		case kAVCSignalModeDVCPro100_625_50:
3968			packetBufferSize = 1932;
3969			alignedDVPacketSize = 2048;
3970			break;
3971
3972		case kAVCSignalModeSD525_60:
3973		case kAVCSignalModeDVCPro525_60:
3974		case kAVCSignalModeSD625_50:
3975		case kAVCSignalModeDVCPro625_50:
3976		default:
3977			packetBufferSize = 492;
3978			alignedDVPacketSize = 512;
3979			break;
3980	};
3981
3982	pingPongBufferSize = kNumPingPongs * kNumPacketsPerPingPong * alignedDVPacketSize;
3983
3984    // Create ping pong buffer, overrun buffer and time stamp buffer
3985    //zzz should allocate in initialization routine.
3986    bufferSize = pingPongBufferSize + alignedDVPacketSize + kNumPingPongs * sizeof(UInt32);
3987    vm_allocate(mach_task_self(), (vm_address_t *)&pingPongBuffer,
3988        bufferSize, VM_FLAGS_ANYWHERE);
3989    if (pingPongBuffer == NULL)
3990    {
3991        // syslog(LOG_INFO, "DVRead::Start : IOMalloc: pingPongBuffer failed\n");
3992        res = kIOReturnNoMemory;
3993        goto bail;
3994    }
3995    timeStampPtr = (UInt32 *)(pingPongBuffer + pingPongBufferSize + alignedDVPacketSize);
3996    globs->fStreamVars.fDCLBuffers = pingPongBuffer;
3997    globs->fStreamVars.fDCLBufferSize = bufferSize;
3998    bzero( pingPongBuffer, bufferSize );
3999
4000    // Get pointer to start of DCL commands and update list.
4001    opcodes = (DCLCommandPtr)malloc(kRecordDCLProgramSize);
4002    globs->fStreamVars.pDCLList = opcodes;
4003    if (opcodes == NULL)
4004    {
4005        // syslog(LOG_INFO, "DVRead::Start : IOMalloc: opcodes failed\n");
4006        res = kIOReturnNoMemory;
4007        goto bail;
4008    }
4009    bzero( opcodes, kRecordDCLProgramSize );
4010    pDCLCommand = (UInt8 *)opcodes;
4011    updateDCLList = (DCLCommandPtr *)malloc(kRecordNumDCLs * sizeof(DCLCommandPtr));
4012    globs->ppUpdateDCLList = updateDCLList;
4013    if (updateDCLList == NULL)
4014    {
4015        // syslog(LOG_INFO, "DVRead::Start : IOMalloc: updateDCLList failed\n");
4016        res = kIOReturnNoMemory;
4017        goto bail;
4018    }
4019    bzero( updateDCLList, kRecordNumDCLs * sizeof(DCLCommandPtr));
4020
4021    // Create label for start of loop.
4022    pStartDCLLabel = (DCLLabelPtr) pDCLCommand;
4023    pDCLCommand += sizeof (DCLLabel);
4024    pStartDCLLabel->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
4025    pStartDCLLabel->opcode = kDCLLabelOp;
4026    pingPongPtr = pingPongBuffer;
4027
4028    // Create kNumPingPongs ping pong buffer lists of 100 packets each.
4029    for (pingPongNum = 0; pingPongNum < kNumPingPongs; pingPongNum++)
4030    {
4031            // Create the DCL input record record and fill it in.
4032            pLocalData = &globs->fLocalDataArray[pingPongNum];
4033            //pLocalData->fFirstCmd = (DCLCommandPtr) pDCLCommand;
4034            pLocalData->pGlobalData = globs;
4035            pLocalData->fBlockNum = pingPongNum;
4036            pLocalData->fTimeStampPtr = timeStampPtr;
4037            *timeStampPtr = 0xffffffff;
4038            startUpdateDCLList = updateDCLList;
4039            updateListSize = 0;
4040            // Create block start label
4041            pBlockDCLLabel = (DCLLabelPtr) pDCLCommand;
4042            pDCLCommand += sizeof (DCLLabel);
4043            pBlockDCLLabel->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
4044            pBlockDCLLabel->opcode = kDCLLabelOp;
4045            pLocalData->fStateLabel = pBlockDCLLabel;
4046
4047            pLocalData->fFirstCmd = (DCLCommandPtr) pDCLCommand;
4048
4049            // Create transfer DCL for each packet.
4050            for (packetNum = 0; packetNum < kNumPacketsPerPingPong; packetNum++)
4051            {
4052                    // Receive one packet up to packetBufferSize bytes.
4053                    pDCLTransferPacket = (DCLTransferPacketPtr) pDCLCommand;
4054                    pDCLCommand += sizeof (DCLTransferPacket);
4055                    pDCLTransferPacket->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
4056                    pDCLTransferPacket->opcode = kDCLReceivePacketStartOp;
4057                    pDCLTransferPacket->buffer = pingPongPtr;
4058                    pDCLTransferPacket->size = packetBufferSize;
4059
4060                    *updateDCLList++ = (DCLCommandPtr) pDCLTransferPacket;
4061                    updateListSize++;
4062                    pingPongPtr += alignedDVPacketSize;
4063            }
4064
4065            // Create timestamp.
4066            pDCLTimeStamp = (DCLPtrTimeStampPtr) pDCLCommand;
4067            pDCLCommand += sizeof (DCLPtrTimeStamp);
4068            pDCLTimeStamp->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
4069            pDCLTimeStamp->opcode = kDCLPtrTimeStampOp;
4070            pDCLTimeStamp->timeStampPtr = timeStampPtr++;
4071            *updateDCLList++ = (DCLCommandPtr) pDCLTimeStamp;
4072            updateListSize++;
4073            // Create update DCL list.
4074            pDCLUpdateDCLList = (DCLUpdateDCLListPtr) pDCLCommand;
4075            pDCLCommand += sizeof (DCLUpdateDCLList);
4076            pDCLUpdateDCLList->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
4077            pDCLUpdateDCLList->opcode = kDCLUpdateDCLListOp;
4078            pDCLUpdateDCLList->dclCommandList = startUpdateDCLList;
4079            pDCLUpdateDCLList->numDCLCommands = updateListSize;
4080
4081            // Jump to next block (to terminator for last block)
4082            pDCLPingPongLoop = (DCLJumpPtr) pDCLCommand;
4083            pDCLCommand += sizeof (DCLJump);
4084            pDCLPingPongLoop->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
4085            pDCLPingPongLoop->opcode = kDCLJumpOp | kFWDCLOpDynamicFlag;
4086            pDCLPingPongLoop->pJumpDCLLabel = (DCLLabelPtr)pDCLCommand;
4087            pLocalData->fStateJmp = pDCLPingPongLoop;
4088
4089    }
4090
4091    // Create label for underrun.
4092    pUnderrunDCLLabel = (DCLLabelPtr) pDCLCommand;
4093    //pGlobalData->pUnderrunDCLLabel = pUnderrunDCLLabel;
4094    pDCLCommand += sizeof (DCLLabel);
4095    pUnderrunDCLLabel->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
4096    pUnderrunDCLLabel->opcode = kDCLLabelOp;
4097    globs->fTerminal = pUnderrunDCLLabel;
4098
4099    // Create receive command for underrun packet
4100    pDCLTransferPacket = (DCLTransferPacketPtr) pDCLCommand;
4101    pDCLCommand += sizeof (DCLTransferPacket);
4102    pDCLTransferPacket->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
4103    pDCLTransferPacket->opcode = kDCLReceivePacketStartOp;
4104    pDCLTransferPacket->buffer = pingPongPtr;
4105    pDCLTransferPacket->size = packetBufferSize;
4106
4107    // Call underrun proc.
4108    pUnderrunDCLCallProc = (DCLCallProcPtr) pDCLCommand;
4109    pDCLCommand += sizeof (DCLCallProc);
4110    pUnderrunDCLCallProc->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
4111    pUnderrunDCLCallProc->opcode = kDCLCallProcOp;
4112    pUnderrunDCLCallProc->proc = DVReadHandleInputUnderrun;
4113    pUnderrunDCLCallProc->procData = (UInt32)globs;
4114
4115    // Create closed loop at end to flush isoc packets out of FIFO
4116
4117    pUnderrunDCLLabel = (DCLLabelPtr) pDCLCommand;
4118    pDCLCommand += sizeof (DCLLabel);
4119    pUnderrunDCLLabel->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
4120    pUnderrunDCLLabel->opcode = kDCLLabelOp;
4121
4122    // Create receive command for underrun packet
4123    pDCLTransferPacket = (DCLTransferPacketPtr) pDCLCommand;
4124    pDCLCommand += sizeof (DCLTransferPacket);
4125    pDCLTransferPacket->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
4126    pDCLTransferPacket->opcode = kDCLReceivePacketStartOp;
4127    pDCLTransferPacket->buffer = pingPongPtr;
4128    pDCLTransferPacket->size = packetBufferSize;
4129
4130    // Loop back to keep dumping packets into the bucket
4131    // This is the last command.
4132    pDCLPingPongLoop = (DCLJumpPtr) pDCLCommand;
4133    pDCLPingPongLoop->pNextDCLCommand = NULL;
4134    pDCLPingPongLoop->opcode = kDCLJumpOp;
4135    pDCLPingPongLoop->pJumpDCLLabel = pUnderrunDCLLabel;
4136
4137    // Initialize isochronous channel.
4138    res = openStream(&globs->fStreamVars, false, kDVSDPayloadPacketSize + kDVPacketCIPSize);
4139    if(res != kIOReturnSuccess)
4140        goto bail;
4141
4142    for(i=0; i<kDVMaxStreamsActive; i++) {
4143        if(globs->fStreamVars.fThread->fInStreams[i] == NULL) {
4144            globs->fStreamVars.fThread->fInStreams[i] = globs;
4145            break;
4146        }
4147    }
4148
4149     //syslog(LOG_INFO, "DVRead::Started()\n");
4150
4151#ifdef kIDH_Verbose_Debug_Logging
4152	syslog(LOG_INFO, "DVLib: DVReadStart end\n");
4153#endif
4154
4155    return kIOReturnSuccess;
4156
4157bail:
4158    syslog(LOG_INFO, "DVRead::Start() failed: 0x%x\n", res);
4159    //Stop();
4160
4161#ifdef kIDH_Verbose_Debug_Logging
4162	syslog(LOG_INFO, "DVLib: DVReadStart end:bail\n");
4163#endif
4164
4165    return res;
4166}
4167
4168static IOReturn doDVReadStop(DVGlobalInPtr pGlobalData)
4169{
4170    int i;
4171
4172#ifdef kIDH_Verbose_Debug_Logging
4173	syslog(LOG_INFO, "DVLib: doDVReadStop begin\n");
4174#endif
4175
4176    pGlobalData->dvReadStopInProgress = true;
4177
4178    //syslog(LOG_INFO, "doDVReadStop()0x%x\n", pGlobalData);
4179    for(i=0; i<kDVMaxStreamsActive; i++) {
4180        if(pGlobalData->fStreamVars.fThread->fInStreams[i] == pGlobalData) {
4181            pGlobalData->fStreamVars.fThread->fInStreams[i] = NULL;
4182            break;
4183        }
4184    }
4185
4186#ifdef USE_P2P_CONNECTIONS_FOR_DV_READ
4187    BreakP2PConnectionForRead(pGlobalData->fStreamVars.pDVDevice,0,pGlobalData->fStreamVars.fIsocChannel);
4188#endif
4189
4190    closeStream(&pGlobalData->fStreamVars);
4191    if ( pGlobalData->ppUpdateDCLList) {
4192        free( pGlobalData->ppUpdateDCLList); //,kRecordNumDCLs * sizeof(DCLCommandPtr));
4193        pGlobalData->ppUpdateDCLList = NULL;
4194    }
4195    if ( pGlobalData->fStreamVars.pDCLList) {
4196        //bzero(pGlobalData->fStreamVars.pDCLList, kRecordDCLProgramSize);
4197        free( pGlobalData->fStreamVars.pDCLList); //, kRecordDCLProgramSize);
4198        pGlobalData->fStreamVars.pDCLList = NULL;
4199    }
4200    if ( pGlobalData->fStreamVars.fDCLBuffers) {
4201        vm_deallocate(mach_task_self(), (vm_address_t)pGlobalData->fStreamVars.fDCLBuffers,
4202                pGlobalData->fStreamVars.fDCLBufferSize);
4203        pGlobalData->fStreamVars.fDCLBuffers = NULL;
4204    }
4205
4206    pGlobalData->dvReadStopInProgress = false;
4207
4208#ifdef kIDH_Verbose_Debug_Logging
4209	syslog(LOG_INFO, "DVLib: doDVReadStop end\n");
4210#endif
4211
4212    return kIOReturnSuccess;
4213}
4214
4215void DVReadStop(DVGlobalInPtr pGlobalData)
4216{
4217#ifdef kIDH_Verbose_Debug_Logging
4218	syslog(LOG_INFO, "DVLib: DVReadStop begin\n");
4219#endif
4220
4221    DVRequest(pGlobalData->fStreamVars.fThread, doDVReadStop, pGlobalData, 0);
4222
4223#ifdef kIDH_Verbose_Debug_Logging
4224	syslog(LOG_INFO, "DVLib: DVReadStop end\n");
4225#endif
4226
4227}
4228
4229void DVReadFreeFrames(DVGlobalInPtr globs)
4230{
4231#ifdef kIDH_Verbose_Debug_Logging
4232	syslog(LOG_INFO, "DVLib: DVReadFreeFrames begin\n");
4233#endif
4234
4235    DVFreeFrames(&globs->fStreamVars.fFrames);
4236
4237#ifdef kIDH_Verbose_Debug_Logging
4238	syslog(LOG_INFO, "DVLib: DVReadFreeFrames end\n");
4239#endif
4240
4241}
4242
4243void DVReadFree(DVGlobalInPtr globs)
4244{
4245
4246#ifdef kIDH_Verbose_Debug_Logging
4247	syslog(LOG_INFO, "DVLib: DVReadFree begin\n");
4248#endif
4249
4250    // Defer freeing of the globalin data struct
4251    // if we have a pending input underrun to deal with.
4252
4253    if (globs->pendingDVReadUnderrunHandler == true)
4254	globs->deferredDVReadFree = true;
4255    else
4256	free(globs);
4257
4258#ifdef kIDH_Verbose_Debug_Logging
4259	syslog(LOG_INFO, "DVLib: DVReadFree end\n");
4260#endif
4261
4262}
4263
4264void DVLog(DVThread *thread, UInt32 tag, CFAbsoluteTime start, CFAbsoluteTime end)
4265{
4266#if TIMING
4267    Log * log;
4268
4269    log = &thread->fLog[thread->fLogPos];
4270    log->tag = tag;
4271    log->start = start;
4272    log->end = end;
4273    thread->fLogPos++;
4274    if(thread->fLogPos >= kLogSize)
4275        thread->fLogPos = 0;
4276#endif
4277}
4278
4279void DVDumpLog(DVThread *thread)
4280{
4281#if TIMING
4282    Log * log;
4283    UInt32 tag;
4284    int i;
4285
4286    for(i=thread->fLogPos; i<kLogSize; i++) {
4287        log = &thread->fLog[i];
4288        tag = log->tag;
4289        if(tag) {
4290            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);
4291        }
4292        else
4293            syslog(LOG_INFO, "%d %x %8.3f to %8.3f\n", i, tag, log->start, log->end);
4294    }
4295
4296    for(i=0; i< thread->fLogPos; i++) {
4297        log = &thread->fLog[i];
4298        tag = log->tag;
4299        if(tag) {
4300            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);
4301        }
4302        else
4303            syslog(LOG_INFO, "%d %x %8.3f to %8.3f\n", i, tag, log->start, log->end);
4304    }
4305#endif
4306}
4307