1/*
2	File:		DVIsochComponent.c
3
4	Contains:	DV version of the Isoch component
5
6	Version:	1.0
7
8	Copyright:	� 1999-2000 by Apple Computer, Inc., all rights reserved.
9
10	File Ownership:
11
12		DRI:				Richard Sepulveda
13
14		Other Contact:		Jay Lloyd, Kevin Williams
15
16		Technology:			DV
17
18	Writers:
19
20		(CK)	Casey King
21		(SF)	Sean Findley
22		(KW)	Kevin Williams
23		(jkl)	Jay Lloyd
24		(GDW)	George D. Wilson Jr.
25		(SW)	Sean Williams
26		(RS)	Richard Sepulveda
27
28	Change History (most recent first):
29
30	  <DV86>	 3/16/00	SF		[2438610] Put 1-second delay before checking camera
31									configuration.
32	  <DV85>	 2/14/00	CK		[2432944]  A little more to kill this bug <sf,cp>
33	  <DV84>	 2/10/00	SF		[2432944] Add retries to bandwidth/channel allocations.
34	  <DV83>	 12/7/99	jkl		Changed isocMedia to isochMedia.
35	  <DV82>	 11/1/99	jkl		Added useCMP atom for turning on and off CMP. Moved CMP from
36									device specific to global. Moved the check for useCMP in the
37									resource to global allocate time. Changed CMPWrite to update the
38									expectedValue after each attempt to try to make it succeed. Some
39									time the Canon camera arbitrarily fails on this. This needs to
40									be redone so the entire operation can make sure the values are
41									correct.
42	  <DV81>	10/25/99	KW		If an error occurs during one of the calls to FSL during
43									cmpResetStage1..9, set the reset stage to kNoResetsProcessing.
44									This ensures that the next bus reset will be processed.
45	  <DV80>	10/22/99	KW		In updateDeviceList, do not zero out localFWReferenceID when a
46									device is removed. Zeroing out the localFWReferenceID causes the
47									cmp bus reset routines to stop propogating. In doAVCTransaction
48									deallocated the command object in the event of an error.
49	  <DV79>	 9/28/99	KW		In IDHRead and IDHWrite, do not tear down read/write
50									notification if an error occurs.
51	  <DV78>	 9/21/99	jkl		Backed out this read/write postEvent deferred stuff. A
52									stack-based variable was killing it and it needs more analysis.
53	  <DV77>	 9/20/99	jkl		Really made the previous change. The DVFamilyLib postEvent was
54									commented as meaning to defer the rest of the read/write
55									complete notification to another deferred task, but was calling
56									CallSecondaryInterruptHandler2 to do it. Since the postEvents
57									for the read/write complete are already at deferred task, this
58									call basically did nothing. Now QueueSecondaryInterruptHandler
59									is used which definitely postpones the completion of postEvent
60									to another deferred task. This is now much more of a risky
61									change.
62		<76>	 9/17/99	jkl		Changed isoch read/write complete notification callProcs to be
63									called in another SIH.
64		<75>	 8/29/99	RS		Fixed problem with unfriendly clients who wouldn't cancel their
65									notifications before closing the isoch component. This could
66									cause a crash if we callback and the client isn't there anymore.
67
68		<74>	 8/28/99	KW		Dispose of all clients signed up for notifications when last
69									instance is closed. Fixes crashes for hot plugging.
70		<73>	 8/17/99	jkl		Changed CMP check to look for file type of INIT instead of gdfx.
71		<72>	 8/16/99	RS		Added isoch version atom to device list.
72		<71>	 8/16/99	jkl		Changed useCMP function to be off by default and look for a
73									'ucmp' resource id -20756 before using CMP. FireWire 2.2 or
74									greater check is still there.
75		<70>	 8/16/99	jkl		Replicated a default DV name atom property in old name property
76									location. Moved the bus reset notification proc with FireWire to
77									only be active during the isoch read and write. This makes sure
78									an instance is always open with the call proc address valid.
79		<69>	 8/12/99	jkl		Bracketed the name QTCopyAtomToPtr with lock/unlock container
80									calls.
81		<68>	 8/10/99	jkl		Added camera name updating routine.
82		<67>	  8/9/99	RS		Added IDHUpdateDeviceList() to API. The client can now change
83									r/w atoms in the internal device list. This currently includes
84									device name. Completed the bus reset generated IDHDeviceChanged
85									notification. The client can now be notified at interrupt time
86									whether a device state has been changed.
87		<66>	  8/9/99	jkl		Added handleBusReset call proc for interrupt level bus reset
88									notification.
89		<65>	  8/9/99	jkl		Added some camera name stuff. Moved the name atom to be a direct
90									child of the device atom. Default name is DV, if a name has been
91									entered for the device then use it, else if the vendor is known
92									then name is "vendor name DV".
93		<64>	  8/6/99	jkl		More work to get rid of DVFamily.h. Added defines for
94									getDeviceStandard AVC transaction. Also changed default camera
95									name to DV and left off standard.
96		<63>	  8/6/99	jkl		Changed kNTSCStandard to be ntscIn from QTComponents.h.
97		<62>	  8/3/99	RS		Properly handling device disconnect by propogating errors to
98									client(s).
99		<61>	 7/29/99	jkl		Set input channel when camera is listener during bus reset
100									handling. Some cameras do not follow the spec. Made sure to set
101									bus reset processing state to noProcessing after bus reset
102									processing has been interrupted.
103		<60>	 7/28/99	jkl		Added all of the bus reset stages for CMP. Still not robust
104									enough to handle errors during bus reset. Added Gestalt check
105									for FireWire 2.2 or greater. CMP does not run if this check
106									fails, allowing HaveBlue to run on FireWire 2.1.
107		<59>	 7/28/99	jkl		Changed FWRegisterDriver to use the device description instead
108									of driver refNum for client specific data. This lets the bus
109									reset handling get the device description. Nobody was using the
110									refNum. Turned on bus reset management.
111		<58>	 7/28/99	jkl		Moved isochPortCommandObject and asynchCommandObject allocation
112									to updateDeviceList, device added routine. Set max payload for
113									asynch command object to 512. Changed all of the cmp routines to
114									use the already allocated asynch command object. Changed local
115									pcrBuffer modifications to always use FWRead and
116									FWCompareAndSwap. Reduced overhead ID from 14 to 12.
117		<57>	 7/23/99	jkl		Turned off CMP bus reset handling.
118		<56>	 7/22/99	jkl		CMP, added correct speed, bandwidth, and overheadID calculations
119									and configuration to manage reconnections after bus reset.
120		<55>	 7/20/99	jkl		Fixed CMP bug where device not broadcasting could look
121									connected. Turned off broadcast bit to disconnect plug before
122									changing channel on a device only broadcasting. Sony is not
123									going to like this but it solves compatibility problems and
124									strictly follows the spec.
125		<54>	 7/20/99	jkl		Turned CMP on. Can create a file in Preferences folder called
126									"no cmp" to disable CMP.
127		<53>	 7/18/99	jkl		Fixed command object allocation in irmAllocateBandwidth. Fixed
128									initial overhead ID value for output plug control register.
129		<52>	 7/16/99	jkl		Added latest CMP code.
130		<51>	 7/16/99	KW		Moved FireWire callbacks to here. Register all devices with
131									FireWire that have ever been seen when the first isoch component
132									is opened. Likewise, Unregister all devices when the last isoch
133									component is closed. Ensure that the device notification ID for
134									a device is zeroed out after the notification has been disposed
135									of.
136		<50>	 7/14/99	jkl		Fixed dclProgramID mistake.
137		<49>	 7/14/99	jkl		Made sure isochPortCommandObjects are allocated safely and not
138									prematurely deallocated. Made sure disable read and write are
139									safe if extraneous calls are made to them.
140		<48>	 7/13/99	jkl		Reverted to previous version. Added fix for device control
141									getTimeCode failing after hot plug.
142		<47>	 7/12/99	jkl		Added a bunch of CMP stuff. Almost ready to turn on.
143		<46>	 7/12/99	RS		Changed component instance storage from system heap to
144									application heap to work around misbehaved apps that don't
145									CloseComponent.
146		<45>	 7/10/99	RS		Added notification for device added/removed so that we can
147									update the fwClientID in the device control component if the
148									camera is removed then re-added.
149		<44>	  7/8/99	RS		Fixed problem where the device control instance was being
150									prematurely released before all of the clients were finished.
151	  <OX43>	  7/7/99	SW		Just removed some unused variables to stop compiler warnings.
152		<42>	  7/7/99	jkl		Cleared driverRefNum and fwClientID fields on device removed,
153									and updated them on device added for an already known device.
154									Also updated localFWReferenceID in case the same camera is
155									plugged into a different FireWire interface on the machine.
156		<41>	  7/5/99	jkl		Added SetMaxPayload for client.
157		<40>	  7/2/99	RS		Replaced event recording with logger to increase debug
158									capability.
159		<39>	  7/1/99	RS		Passing actual device description pointer instead of client
160									instance to isoch callbacks since the client can change deviceID
161									behind our back.
162		<38>	 6/30/99	RS		Fixed error codes returned in certain functions. Removed
163									openDeviceConnection() function because it is no long needed.
164									Fixed bug with device name atom. Fixed bug where we were
165									disabling read/writes in Close() if device was already possibly
166									disabled.
167		<37>	 6/28/99	RS		Moved IDHGetClientID from public to private call. Added
168									kIDHErrDeviceTimeout errors for sync read/writes timeouts.
169									Created public structs for IDHDimension and IDHResolution. Added
170									deviceID to atom tree.
171		<36>	 6/28/99	RS		Moved open device control from OpenDevice to SetConfig so that
172									client doesn't have to Open a device for read/write in order to
173									device control. Added device enable/disable notification.
174									Returning error on getConfig if no previous setConfig.
175		<35>	 6/27/99	jkl		Added default to NTSC in getDeviceStandard for devices that may
176									not have any device control capability.
177		<34>	 6/26/99	jkl		Integrated Richard's, Kevin's, and my changes to get read and
178									write working. Wired clock back up. Too much other stuff to
179									mention. Compare revisions if you really want to know.
180		<33>	 6/24/99	RS		Added the DVCWriteFrame() and DVGetNextEmptyFrame() equivalent.
181									We think all of the plumbing is in. So let the fun debug begin.
182		<32>	 6/24/99	KW		Not ready for prime time but things compile. Errors exist.
183		<31>	 6/24/99	jkl		Yanked all of the DV driver code into here. Some fun.
184	  <OX30>	 6/22/99	SW		Add support for private calls to the IDH DV component instead of
185									just using morphed GetDeviceStatus() calls.
186		<29>	 6/21/99	RS		Added timeouts to synchronous reads and writes. The timeout has
187									initially been set to 1/15 second (2 frame times).
188		<28>	 6/20/99	RS		Moved device atom to device description structure to correctly
189									handle device added/removed issues. Added PAL audio format
190									config. Initialize device atom seed to 1. More cleanup.
191		<27>	 6/19/99	RS		Removed getting connectionID in IDHGetDeviceStatus (not needed).
192		<26>	 6/19/99	RS		Added deviceAtom and device standard to device description
193									structure. Allocating correct buffer size according to camera
194									format (ntsc or pal). Fixed bug where deviceID could change with
195									last connection going to zero (BAD). Alot of general cleanup.
196		<25>	 6/18/99	RS		Added kIDHUniqueIDType to device atom tree. Added some auxillary
197									functions to help traverse atom list.
198		<24>	 6/18/99	GDW		Made device control component handling global verses per
199									instance.
200		<23>	 6/18/99	GDW		Made some changes for additional device control component.
201		<22>	 6/17/99	RS		Made some changes to open and close device to handle device
202									access a little more gracefully.
203		<21>	 6/17/99	RS		Added FWClientID to device descriptor for device control. Fixed
204									bug when groing from write to read mode on a camera. Buffers
205									weren't being allocated.
206		<20>	 6/17/99	jkl		Added fwClientID to DVGetLocalFWReferenceID call to make the
207									fwClientID part of the device info so IDH can execute
208									getDeviceStandard by itself.
209		<19>	 6/16/99	RS		Added Firewire Node ID to device structure. Added AVC
210									transaction code to isoch component to work around DVDriver hang
211									problem. Check for sync I/O problem when called at non-task
212									time.
213		<18>	 6/17/99	GDW		Added more device control calls.
214		<17>	 6/16/99	RS		Do parameter checking on all public IDH functions. Turn on
215									exclusive access checking. Fix bug with I/O direction atom size.
216		<16>	 6/16/99	GDW		Add calls to device control component.
217		<15>	 6/15/99	RS		Updating device list everytime a device is added or removed.
218									Locking down atom container when we are copying data from atom
219									to pointer.
220		<14>	 6/15/99	RS		Added synchronous I/O and passing a 'nil' buffer into IDHWrite.
221		<13>	 6/15/99	RS		Changed IODirection on DV configs from input to InputAndOutput.
222		<12>	 6/15/99	RS		Merged DVFamilyLib into Isoch component. Added seed for device
223									list validity check from client. Create component globals in
224									register and free in unregister. Check seed when client does a
225									SetConfig.
226	  <OX11>	 6/11/99	SW		MasterInterfaces integration
227		<10>	 6/10/99	RS		Added two new IDH functions ReleaseBuffer and CancelIO. Added
228									notification version of Reads and Writes. MAJOR CHANGES.
229		 <9>	  6/8/99	RS		Fixed bug storing IDHDeviceStatus in atom list.
230		 <8>	  6/7/99	RS		Keep track of open for reads/writes per device.
231		 <7>	  6/7/99	RS		Saving open permissions in component instance. Replaced Fail
232									macro with new macro that displays function return code (better
233									for debug). Disable device on close based on permissions flags.
234									Returning bufferlist on read/write completion callback. Fixed
235									bug in IDHWrite where we weren't doing a DVCWriteFrame.
236	   	 <6>	  6/4/99	SW		Adjust for slight changes in IDH.i
237		 <5>	  6/1/99	RS		Added private functions to find device atom given any of its
238									siblings and a function to get device status given a device ID.
239									Added device standard and firewire node ID to status struct.
240									Save open permissions in device status.
241		 <4>	 5/24/99	RS		Changed param type in SetDeviceConfig. Allow user to get device
242									status on any device (not just configured device). Changed
243									IDHIsochReadData and IDHIsochWriteData to IDHReadData and
244									IDHWrite.
245		 <3>	 5/24/99	RS		We are now returning a copy of device descrip atom container.
246		 <2>	 5/20/99	RS		Added queueing of isoch reads and write. Changed error return
247									code names.
248*/
249
250/*							���	TO DO ���
251
252	camera tracking
253	if a camera goes offline, do we return an error from getdevicestatus or set the active flag
254	super find atom
255	turn on permanent locks by client
256	review atom list (i.e. isoch container atom)
257*/
258
259//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
260// Includes
261
262#include <DeviceControl.h>
263#include <DeviceControlPriv.h>
264#include <DriverServices.h>
265#include <Gestalt.h>
266#include <Resources.h>
267#include <Sound.h>
268#include <TextUtils.h>
269#include <DeviceControl.h>
270#include <IsochronousDataHandler.h>
271#include <IsochronousDataHandlerPriv.h>
272#include <Folders.h>
273
274#include <fp.h>
275#include <stddef.h>
276#include <stdio.h>
277
278#include "DVVers.h"
279#include "FireWire.h"
280#include "GenericDriverFamily.h"
281#include "LoggerAndDebugMacros.h"
282#include "OxcartDebug.h"
283#include "DVDCLOut.h"
284#include "DVDCLIn.h"
285
286#define kDVDeviceInfo 				'ddin'	// DV device info
287#define	kBuffersPerDevice			5		// number of buffers per device for reads and writes
288#define kMaxDevicesActive			64		// max devices on firewire bus
289#define kMaxInstances				10		// max clients attached to a device
290#define kMaxNotifications			100		// 100 notifcaionts can be supported
291#define kNTSCCompressedBufferSize	120000
292#define	kPALCompressedBufferSize	144000
293#define kIDHSeedAtomType			'seed'	// seed for device atom validity
294#define kIDHDevicePresentType		'pres'	// is device present?
295#define	kServiceTypeDVServices		'avc '
296#define kTimeoutDuration			(1000 / 15) * durationMillisecond	// 1/15 second (2 frame times)
297#define kMaxRetries					8
298
299#if PRAGMA_ALIGN_SUPPORTED
300#pragma options align=mac68k
301#endif
302
303#define kNoResetsProcessing 	0
304#define kResetProcessing		1
305#define kResetInterrupted		2
306#define kResetFailed			3
307
308#define koPCRaddrHi		0x0000FFFF
309#define koPCRaddrLo		0xF0000904
310#define kiPCRaddrHi		0x0000FFFF
311#define kiPCRaddrLo		0xF0000984
312
313//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
314// globals
315ComponentInstance gCurInstance = 0;
316
317typedef struct IsochComponentInstance	IsochComponentInstance,
318										*IsochComponentInstancePtr;
319typedef struct IsochComponentGlobals 	IsochComponentGlobals,
320										*IsochComponentGlobalsPtr,
321										**IsochComponentGlobalsHandle;
322
323
324struct IDHDVCompleteEvent {
325	IDHEventHeader		eventHeader;
326	Ptr					frameBuffer;
327	UInt32				bufferSize;
328	UInt32				fwCycleTime;
329
330};
331typedef struct IDHDVCompleteEvent IDHDVCompleteEvent;
332
333
334// internal frame queue structure
335typedef struct QueueElement {
336	struct QElem 	*qLink;									// standard queue vars
337	short			qType;
338	short			locks;									// number of clients owning buffer
339	Ptr				buffer;									// the data
340	long			size;									// size of the data
341	} QueueElement, *QueueElementPtr;
342
343// describes a device on the firewire bus
344struct DeviceDescription {
345	IsochComponentGlobals	*componentGlobals;				// isoch component globals
346	QHdrPtr					freeQueue,						// holds free buffers
347							usedQueue;						// holds buffers being used by client
348	IDHDeviceID				deviceID;						// persistent unique ID for each device
349	FWReferenceID			fwClientID;						// volatile FW Client ID for FSL, unique for all active devices
350	FWReferenceID			localFWReferenceID;				// reference to local node, may change if more than one FW interface is available
351	RegEntryID				regEntryID;						// used to register driver with FireWire
352	CSRNodeUniqueID			uniqueID;						// peristent 64-bit EUID for 1394
353	IDHNotificationID 		readIsochNotificationID;		// read notification ID
354	IDHNotificationID 		writeIsochNotificationID;		// write notification ID
355	IsochComponentInstance	*instancePtr[kMaxInstances];	// clients that are attached to device
356	ComponentInstance		deviceControlInstance;			// device control component instance
357	IDHNotificationID		deviceControlNotificationID;	// tell me when device is added/removed so that i can adjust fwClientID
358	Component				clock;							// FireWire clock component, client's get their own instance, not sure why
359	UInt32 					standard; 						// device standard
360	QTAtomContainer			deviceContainer;				// contains device database
361	IsochPortID				isochPortID;					// contains FireWire isoch port ID for active stream
362	IsochChannelID			outputIsochChannelID;			// FireWire channel ID for isoch transmit
363	IsochChannelID			inputIsochChannelID;			// FireWire channel ID for isoch receive
364	DVGlobalOutPtr			pGlobalDVOutData;				// global data for output DCL
365	DVGlobalInPtr			pGlobalDVInData;				// global data for input DCL
366	DCLProgramID			dclProgramID;					// ID of DCL program
367	FWCommandObjectID		isochPortCommandObjectID;		// FireWire command object used in init/release isoch port routines
368	FWCommandObjectID		startIsochPortCommandObjectID;	// FireWire command object used in start isoch port routines
369	FWCommandObjectID		stopIsochPortCommandObjectID;	// FireWire command object used in stop isoch port routines
370	FWCommandObjectID		asynchCommandObjectID;			// FireWire command object used in CMP
371	FWAddressSpaceID		pcrID;							// address space ID of plug control register
372	UInt32					pcrBuffer;						// pcr buffer space, use FWRead and FWLock to access
373	UInt32					pcrIndex;						// pcr number that was allocated
374	UInt32					resetStatus;					// used in bus reset processing to keep up with reset stage
375	UInt32					buffer[2];						// buffer for pcr reads and locks
376	UInt32					expectedPCR;					// value to check for successful update of PCR
377	UInt32					cmpBandwidth;					// allocated isoch bandwidth for a stream, used in re-establishing connection after bus reset
378	UInt32					cmpOverheadID;					// overheadID for a stream, used in re-establishing connection after bus reset
379	UInt32					cmpChannel;						// channel for a stream, used in re-establishing connection after bus reset
380	DriverRefNum			refNum;							// volatile driver refNum, unique for all active devices
381	SInt16					deviceControlCount;				// number of clients using device control
382	SInt16					nQueueElements;					// holds number of buffers avail
383	SInt16					readLocks;						// num clients that have device open for reads
384	SInt16					writeLocks;						// num clients that have device open for writes
385	SInt16					exclusiveAccess;				// does client have exclusive access?
386	Boolean					complete;						// I/O complete flag for synchronous operation
387	Boolean					active;							// device is currently online
388	};
389
390typedef struct DeviceDescription DeviceDescription, *DeviceDescriptionPtr;
391
392struct ClientNotification {
393	ComponentInstance 	ihc;
394	IDHDeviceID			deviceID;
395	IDHNotificationProc	notificationProc;
396	IDHEvent			events;
397	void				*userData;
398	};
399
400typedef struct ClientNotification ClientNotification;
401
402// component globals
403struct IsochComponentGlobals {
404	IsochComponentInstancePtr	lastInstance;				// last instance opened
405	UInt32						useCMP;						// CMP off by default, turn on by adding 'ucmp' resource
406	DeviceDescription			deviceDescription[kMaxDevicesActive];	// description of each device
407	ClientNotification			clientNotification[kMaxNotifications];	// arbirtary # of notifications
408	UInt32						nDevices;					// number of devices in the list
409	UInt32						seed;						// keep track of device lists
410	IsochComponentInstance		*localInstance;				// our local instance
411	Boolean						unRegistered;				// was component unregistered?
412	};
413
414// per component instance variables
415struct IsochComponentInstance {
416	Handle 						instanceHandle;				// instance storage handle
417	ComponentInstance 			self;						// this component
418	IsochComponentGlobalsPtr 	gGlobals;					// pointer to component globals
419	QTAtomSpec					currentConfig;				// current device config set by client
420	IDHDeviceID					deviceID;					// persistent unique ID for each device, seed value
421	long						permissions;				// open permissions
422	QHdrPtr 					readQueue;					// queue of read requests
423	QHdrPtr 					writeQueue;					// queue of write requests
424	Boolean						timeout;					// timeout for synchronous reads
425	Boolean						hasDeviceControl;			// does this client have device control?
426	};
427
428// convenience stuff for getDeviceStandard
429typedef struct AVCCTSFrameStruct {
430	UInt8		cmdType_respCode;			// cmd type/response code
431	UInt8		headerAddress;
432	UInt8		opcode;
433	UInt8		operand[5];
434} AVCCTSFrameStruct, *AVCCTSFrameStructPtr;
435
436enum {
437	kAVCStatusInquiryCommand	= 0x01,
438	kAVCOutputSignalModeOpcode	= 0x78,
439	kAVCInputSignalModeOpcode	= 0x79,
440	kAVCSignalModeSD525_60		= 0x00,
441	kAVCSignalModeSDL525_60		= 0x04,
442	kAVCSignalModeHD1125_60		= 0x08,
443	kAVCSignalModeSD625_50		= 0x80,
444	kAVCSignalModeSDL625_50		= 0x84,
445	kAVCSignalModeHD1250_50		= 0x88,
446	kAVCSignalModeDummyOperand	= 0xff
447};
448
449/************************************************************************************/
450// Begin Dispatch Stuff
451
452// Used by Type's .k.h to create prototypes for our routines
453#define IDH_BASENAME()	IDH
454#define IDH_GLOBALS()	ComponentInstance
455
456#define IDHDV_BASENAME() IDHDV
457#define IDHDV_GLOBALS()	ComponentInstance
458
459#include "IsochronousDataHandler.k.h"
460#include "IsochronousDataHandlerPriv.k.h"
461
462// Used by Component Dispatch Helper to call our routines
463#define	CALLCOMPONENT_BASENAME()		IDH
464#define	CALLCOMPONENT_GLOBALS()			IsochComponentInstancePtr storage
465
466// Other defines for Component Dispatch Helper
467#define	COMPONENT_DISPATCH_FILE			"IDHDispatch.h"		/*  describes what to dispatch */
468
469#define COMPONENT_UPP_SELECT_ROOT()		IDH					/* root for Type's UPP_PREFIX and SELECT_PREFIX */
470#define COMPONENT_SUBTYPE_UPP_SELECT_ROOT()	IDHDV
471
472#include "Components.k.h"				/* std .k.h */
473#include "QuickTimeComponents.k.h"		/* Type & SubType .k.h */
474#include "ComponentDispatchHelper.c"	/* make our dispatcher and cando */
475
476// End Dispatch Stuff
477/************************************************************************************/
478
479// local prototypes
480OSErr		addBuffersToQueue( DeviceDescription *deviceDescriptionPtr);
481OSErr 		checkSeed( IsochComponentGlobalsPtr gGlobals, QTAtomSpec *configID);
482OSErr 		closeDeviceControl( ComponentInstance ih, DeviceDescriptionPtr deviceDescriptionPtr);
483OSStatus 	deviceControlCallback( IDHGenericEvent *event, void *userData);
484OSErr 		doAVCTransaction( FWReferenceID ih, DVCTransactionParams* inTransaction);
485OSErr 		findAtom( QTAtomSpec *atomSpec, OSType theType, QTAtom *theAtom);
486OSErr 		findDeviceDescriptionforDevice( IsochComponentInstancePtr ih, UInt32 deviceID, DeviceDescription **deviceDescription);
487OSErr 		getDeviceID( QTAtomSpec *configID, UInt32 *deviceID);
488OSErr 		getDeviceStandard( FWReferenceID ih, UInt32 * pStandard );
489OSStatus 	localCompletionRoutine(IDHGenericEvent *event, void *userData);
490OSStatus 	myTimerHandler( void *p1, void *p2);
491OSErr 		postEvent(IsochComponentGlobals* gGlobals, IDHDeviceID deviceID,
492				IDHEvent event, void* privateData);
493OSStatus	postEventSIH(void* p1, void* p2);
494OSStatus 	readNotifyProc( IDHGenericEvent *event, void *userData);
495OSErr 		setupAVCTransaction( FWReferenceID ih, DVCTransactionParams *pParams);
496OSErr 		setupVideoAtoms( QTAtomContainer container, QTAtom isocAtom, UInt32 standard);
497OSErr		setup32kAudioAtoms( QTAtomContainer container, QTAtom isocAtom, UInt32 standard);
498OSErr 		setup44kAudioAtoms( QTAtomContainer container, QTAtom isocAtom, UInt32 standard);
499OSErr 		setup48kAudioAtoms( QTAtomContainer container, QTAtom isocAtom, UInt32 standard);
500OSStatus	writeNotifyProc( IDHGenericEvent *event, void *userData);
501OSStatus	cameraNameLookup(DeviceDescriptionPtr pDeviceDescription, UInt8 *name);
502OSStatus	updateCameraName(CSRNodeUniqueID *guid, UInt8 *name);
503OSErr		updateDeviceList(IsochComponentInstancePtr ih, GDFDeviceEventData *pGDFData, IDHDeviceID *deviceID, Boolean doAdd);
504OSStatus	handleDeviceAdded(ComponentInstance ihc, GDFDeviceEventData *pGDFData);
505OSStatus	handleDeviceRemoved(ComponentInstance ihc, GDFDeviceEventData *pGDFData);
506OSStatus	initIsochPort( FWClientInitIsochPortParams *pInitIsochPortParams, UInt32* outCommandAcceptance);
507OSStatus	releaseIsochPort(FWClientReleaseIsochPortParams *pReleaseIsochPortParams, UInt32* outCommandAcceptance);
508OSStatus	stopIsochPort( FWClientIsochPortControlParams *pIsochPortControlParams, UInt32* outCommandAcceptance);
509OSStatus	startIsochPort(FWClientIsochPortControlParams *pIsochPortControlParams, UInt32* outCommandAcceptance);
510void 		isochPortCommandCompletionProc(FWCommandObjectID fwCommandObjectID, OSStatus commandStatus, UInt32 completionProcData);
511OSStatus 	doLocalTalkerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams);
512OSStatus 	doRemoteTalkerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams);
513OSStatus 	trialLocalTalkerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams);
514OSStatus 	trialRemoteTalkerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams);
515OSStatus 	doLocalListenerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams);
516OSStatus 	doRemoteListenerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams);
517OSStatus 	trialLocalListenerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams);
518OSStatus 	trialRemoteListenerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams);
519OSErr		cmpNewPointToPointConnection(UInt32 pcrOffset, FWClientID clientID, UInt32 channel, UInt32 speed, UInt32 overhead, DeviceDescriptionPtr pDeviceDescription);
520OSErr		cmpDisposePointToPointConnection(UInt32 pcrOffset, FWClientID clientID, DeviceDescriptionPtr pDeviceDescription);
521OSStatus	irmReleaseChannel(FWClientID fwClientID, UInt32 channel, DeviceDescriptionPtr pDeviceDescription);
522OSStatus	irmReleaseBandwidth(FWClientID fwClientID, UInt32 fwBandwidthUnits, DeviceDescriptionPtr pDeviceDescription);
523OSStatus	irmAllocateChannel(FWClientID fwClientID, UInt32 *channel, DeviceDescriptionPtr pDeviceDescription);
524OSStatus	irmAllocateBandwidth(FWClientID fwClientID, UInt32 fwBandwidthUnits, DeviceDescriptionPtr pDeviceDescription);
525UInt32		irmCalculateBandwidthUnits(UInt32 pcrValue);
526OSStatus	cmpHandleLocalLock(FWClientAsynchRequestParamsPtr pFWClientAsynchRequestParams, UInt32 *pCommandAcceptance);
527OSErr		cmpRead(UInt32 pcrOffset, FWClientID clientID, UInt32 *pcrValue, DeviceDescriptionPtr pDeviceDescription);
528OSErr		cmpWrite(UInt32 pcrOffset, FWClientID clientID, UInt32 expectedPCRValue, UInt32 newPCRValue, DeviceDescriptionPtr pDeviceDescription);
529OSStatus	cmpHandleBusReset(FWClientInterfaceParamsPtr pParams, UInt32 *pCommandAcceptance);
530OSErr		cmpResetRead(UInt32 offsetLo, FWClientID clientID, FWCommandCompletionProcPtr completionProc, DeviceDescriptionPtr deviceDescriptionPtr);
531OSErr		cmpResetWrite(UInt32 offsetLo, FWClientID clientID, FWCommandCompletionProcPtr completionProc, DeviceDescriptionPtr deviceDescriptionPtr);
532void		cmpResetStageOne(DeviceDescriptionPtr pDeviceDescription);
533void		cmpResetStageTwo(FWCommandObjectID theObjectID, OSStatus status, UInt32 data);
534void		cmpResetStageThree(FWCommandObjectID theObjectID, OSStatus status, UInt32 data);
535void		cmpResetStageFour(FWCommandObjectID theObjectID, OSStatus status, UInt32 data);
536void		cmpResetStageFive(FWCommandObjectID theObjectID, OSStatus status, UInt32 data);
537void		cmpResetStageSix(FWCommandObjectID theObjectID, OSStatus status, UInt32 data);
538void		cmpResetStageSeven(FWCommandObjectID theObjectID, OSStatus status, UInt32 data);
539void		cmpResetStageEight(FWCommandObjectID theObjectID, OSStatus status, UInt32 data);
540void		cmpResetStageNine(FWCommandObjectID theObjectID, OSStatus status, UInt32 data);
541void		cmpResetComplete(FWCommandObjectID theObjectID, OSStatus status, UInt32 data);
542OSErr		enableRead(IsochComponentInstancePtr ih);
543OSErr		enableWrite(IsochComponentInstancePtr ih);
544OSErr		disableRead(IsochComponentInstancePtr ih);
545OSErr		disableWrite(IsochComponentInstancePtr ih);
546OSErr 		dclInitOutput(DeviceDescriptionPtr pDeviceDescription, IsochComponentGlobals* isochComponentGlobals);
547DVLocalOutPtr		dclAllocatePlayBufferGroup(DVGlobalOutPtr pGlobalData);
548DCLCommandPtr		dclAllocateCommand(DCLCommandPoolPtr pDCLCommandPool, UInt32 dclSize);
549DCLCommandBlockPtr	dclAllocateCommandBlock(DCLCommandPoolPtr pDCLCommandPool);
550DCLCommandPoolPtr	dclAllocateCommandPool(void);
551void 				dclDeallocateCommandPool( DCLCommandPoolPtr pDCLCommandPool );
552void				dclDeallocatePlayBufferGroup( DVLocalOutPtr	pLocalData );
553void		dclDeallocateCommandBlock(DCLCommandBlockPtr pDCLCommandBlock);
554void 		handleDVOutput(DCLCommandPtr pDCLCommandPtr);
555void		handleDVOutputUnderrun(DCLCommandPtr pDCLCommandPtr);
556void		updateDVOutputBuffers(DCLCommandPtr pDCLCommandPtr);
557void		handleDVOutputUnderrunCompletion(FWCommandObjectID fwCommandObjectID, OSStatus commandStatus, UInt32 completionProcData);
558OSErr		createDVPlayBufferGroupUpdateList( DVLocalOutPtr pLocalData);
559OSErr		disposeDCLOutput( DVGlobalOutPtr pOutputData );
560UInt32		addFWCycleTimeToFWCycleTime( UInt32 cycleTime1, UInt32 cycleTime2 );
561UInt32		subtractFWCycleTimeFromFWCycleTime( UInt32 cycleTime1, UInt32 cycleTime2 );
562UInt32		convertFractionalSecondsToFWCycleTime( UInt32 secondsNumerator, UInt32 secondsDenominator );
563OSErr		getNextFullOutputFrame( DVIODataPtr pOData, UInt32** ppFrame );
564OSErr		getNextEmptyOutputFrame( DVIODataPtr pOData, UInt32** ppFrame);
565OSErr		queueNextFullOutputFrame( DVIODataPtr pOData, UInt32* pFrame );
566OSErr		getNextEmptyInputFrame( DVIODataPtr pIData, UInt32** ppFrame );
567OSErr 		getNextFullInputFrame( DVIODataPtr pIData, UInt32** ppFrame );
568OSErr 		releaseFullInputFrame( DVIODataPtr pIData, UInt32* pFrame );
569Boolean		isDVFinished( DVIODataPtr pIOData );
570OSErr		getCurrentDCLFrame( DVIODataPtr pIOData, UInt32** ppFrame );
571OSErr 		initDVFrameOutput(UInt32 standard, DVIODataPtr* ppIOData);
572OSErr 		initDVFrameInput(UInt32 standard, DVIODataPtr* ppIOData);
573OSErr 		releaseDVFrameIO( DVIODataPtr pIOData );
574void		deleteDVFrameAndElement( DVFrameQElemPtr pQElem );
575OSErr		dclInitInput(DeviceDescriptionPtr pDeviceDescription, IsochComponentGlobals* isochComponentGlobals);
576void		storeDVPackets( DCLCommandPtr pDCLCommandPtr );
577OSErr		disposeDCLInput( DVGlobalInPtr pInputData );
578OSErr 		writeFrame( IsochComponentInstancePtr ih, char *tmpBuffPtr);
579OSErr		getEmptyOutputFrame( IsochComponentInstancePtr ih, Ptr *tmpBuffPtr, UInt32 *size);
580OSStatus	registerDevice(RegEntryID* inRegEntryID, FWDriverID* outDriverID, DeviceDescriptionPtr pDeviceDescription);
581Boolean		useCMP(void);
582OSStatus	handleBusReset(FWClientInterfaceParamsPtr pParams, UInt32 *pCommandAcceptance);
583
584//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
585// Component Routines
586
587#pragma mark -
588#pragma mark ���������� Standard Component Calls  ����������
589
590//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
591// Reverses anything done in the open call.
592// See this mark for more information on variables handling: GlobalsAndPrefs.
593static pascal ComponentResult IDHClose( IsochComponentInstancePtr iGlobals, ComponentInstance self )
594{
595	IsochComponentGlobalsHandle	globHdl = 0L;							// Handle to our globals
596	OSErr						result = noErr;
597	IsochComponentGlobalsPtr	gGlobals;
598	UInt32						i;
599
600	RecordEventLogger( 'isoc', 'clos', 0, (unsigned long) iGlobals);
601
602	FailMessage(self == 0);
603
604	globHdl = (IsochComponentGlobalsHandle) GetComponentRefcon((Component) self);	// Try and get our refCon
605	if ( (globHdl == nil) || ((UInt32) globHdl == 0xFFFFFFFF) || (iGlobals == nil))	// If its all F's this is a close for register
606	{																		// otherwise it is a real close
607		return(noErr);
608	}
609
610	gGlobals = *globHdl;
611
612	if( iGlobals->deviceID && iGlobals->hasDeviceControl)	// Close device control
613	{
614		DeviceDescription *deviceDescriptionPtr;
615
616		result = findDeviceDescriptionforDevice( iGlobals, iGlobals->deviceID, &deviceDescriptionPtr);
617		FailWithVal( result != noErr, Exit, result);
618
619		result = closeDeviceControl( (ComponentInstance) iGlobals, deviceDescriptionPtr);
620		FailMessage( result != noErr);
621
622		iGlobals->hasDeviceControl = false;
623	}
624
625	if( iGlobals->permissions)
626		IDHCloseDevice( (ComponentInstance) iGlobals);	// close device in case the client forgot to
627
628	for( i=0; i<kMaxNotifications; ++i)	// purge all notifications for this client
629	{
630		if( gGlobals->clientNotification[i].ihc == (ComponentInstance) iGlobals)
631		{
632			RecordEventLogger( 'isoc', 'clos', 'not-', (unsigned long) iGlobals);
633
634			gGlobals->clientNotification[i].deviceID = 0;
635			gGlobals->clientNotification[i].ihc = 0;
636		}
637	}
638
639	PBQueueDelete( iGlobals->readQueue);	// delete queues
640	PBQueueDelete( iGlobals->writeQueue);
641
642	if (CountComponentInstances((Component)self) <= 1)				// Is this the last close?
643	{
644		int 			i;
645		QueueElement	*qep;
646
647		RecordEventLogger( 'isoc', 'clos', 0, 'last');
648
649		for( i=0; i<kMaxDevicesActive; ++i)	// free queue elements
650		{
651			DeviceDescription *deviceDescriptionPtr = &gGlobals->deviceDescription[i];
652
653			// teardown read just in case the client forgot
654			if( deviceDescriptionPtr->deviceID && deviceDescriptionPtr->readIsochNotificationID)
655			{
656				result = IDHCancelNotification( (ComponentInstance)gGlobals->localInstance, deviceDescriptionPtr->readIsochNotificationID);
657				FailMessage( result != noErr);
658
659				result = IDHDisposeNotification( (ComponentInstance)gGlobals->localInstance, deviceDescriptionPtr->readIsochNotificationID);
660				FailMessage( result != noErr);
661
662				deviceDescriptionPtr->readIsochNotificationID = nil;
663
664				if( deviceDescriptionPtr->readLocks > 0)
665				{
666					result = disableRead((IsochComponentInstancePtr) iGlobals);
667					FailMessage( result != noErr);
668
669					deviceDescriptionPtr->readLocks = 0;
670				}
671			}
672
673			// teardown write just in case the client forgot
674			if( deviceDescriptionPtr->deviceID && deviceDescriptionPtr->writeIsochNotificationID)
675			{
676				result = IDHCancelNotification( (ComponentInstance)gGlobals->localInstance, deviceDescriptionPtr->writeIsochNotificationID);
677				FailMessage( result != noErr);
678
679				result = IDHDisposeNotification( (ComponentInstance)gGlobals->localInstance, deviceDescriptionPtr->writeIsochNotificationID);
680				FailMessage( result != noErr);
681
682				deviceDescriptionPtr->writeIsochNotificationID = nil;
683
684				if( deviceDescriptionPtr->writeLocks > 0)
685				{
686					result = disableWrite((IsochComponentInstancePtr) iGlobals);
687					FailMessage( result != noErr);
688
689					deviceDescriptionPtr->writeLocks = 0;
690				}
691			}
692
693			// free all of the queue buffers
694			while( PBDequeueFirst( gGlobals->deviceDescription[i].freeQueue, (QElemPtr *) &qep) == noErr)
695			{
696				if( qep)
697				{
698					if( qep->buffer)
699						DisposePtr( qep->buffer);
700
701					DisposePtr( (char *) qep);
702				}
703			}
704
705			while( PBDequeueFirst( gGlobals->deviceDescription[i].usedQueue, (QElemPtr *) &qep) == noErr)
706			{
707				if( qep)
708				{
709					if( qep->buffer)
710						DisposePtr( qep->buffer);
711
712					DisposePtr( (char *) qep);
713				}
714			}
715
716			gGlobals->deviceDescription[i].readLocks = gGlobals->deviceDescription[i].writeLocks = 0;
717		}
718
719
720		// Unregister all devices that have ever been seen.
721		for (i=0; i < gGlobals->nDevices; ++i)
722		{
723			result = FWUnregisterDriver(gGlobals->deviceDescription[i].fwClientID);
724			FailMessage(noErr != result);
725		}
726
727		// make sure all notifications are cancelled
728		for (i = 0; i < kMaxNotifications; ++i)
729		{
730			if( gGlobals->clientNotification[i].deviceID != 0 &&
731				gGlobals->clientNotification[i].ihc != (ComponentInstance) gGlobals->localInstance)
732			{
733				gGlobals->clientNotification[i].deviceID = 0;
734				log_debugstr( "All notifications weren't cancelled in last close !!!");
735			}
736		}
737
738		if( gGlobals->unRegistered)
739		{
740			for( i=0; i<kMaxDevicesActive; ++i)	// free queue elements
741			{
742				DeviceDescription *deviceDescriptionPtr = &gGlobals->deviceDescription[i];
743
744				result = closeDeviceControl( (ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr);
745				FailMessage( result != noErr);
746
747				if( deviceDescriptionPtr->deviceContainer)	// free device atoms
748				{
749					result = QTRemoveChildren( deviceDescriptionPtr->deviceContainer, kParentAtomIsContainer);
750					FailMessage( result != noErr);
751
752					result = QTDisposeAtomContainer( deviceDescriptionPtr->deviceContainer);
753					FailMessage( result != noErr);
754
755					deviceDescriptionPtr->deviceContainer = nil;
756				}
757			}
758
759			for( i=0; i<kMaxDevicesActive; ++i)
760			{
761				PBQueueDelete( gGlobals->deviceDescription[i].freeQueue);	// delete device queues
762				PBQueueDelete( gGlobals->deviceDescription[i].usedQueue);
763			}
764
765			if( gGlobals->localInstance)
766				DisposePtr( (Ptr) gGlobals->localInstance);
767
768			HUnlock((Handle)globHdl);
769			DisposeHandle((Handle)globHdl);
770			SetComponentRefcon((Component) self, 0xFFFFFFFF);	// component is now unused
771			gGlobals = nil;
772		}
773	}
774
775	if( iGlobals != nil && iGlobals->instanceHandle) // dispose instance handle
776		DisposeHandle( iGlobals->instanceHandle);
777
778Exit:
779	FailMessage( result != noErr);
780
781	return (result);
782}
783
784pascal ComponentResult IDHVersion( IsochComponentInstance *storage)
785{
786#pragma unused (storage)
787
788	RecordEventLogger( 'isoc', 'vers', 0, 0);
789
790	return 0x10000;
791}
792
793pascal ComponentResult IDHTarget(struct IsochComponentInstance *storage, ComponentInstance parentComponent)
794{
795#pragma unused (storage, parentComponent)
796
797	RecordEventLogger( 'isoc', 'targ', 0, 0);
798
799	return(noErr);
800}
801
802
803//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
804// Opens the component instance.
805// See this mark for more information on variables handling: GlobalsAndPrefs.
806static pascal ComponentResult IDHOpen( struct IsochComponentInstance *unused1, ComponentInstance self )
807{
808#pragma unused (unused1)
809
810	IsochComponentInstancePtr		instancePtr = nil;		// Pointer to instance globals
811	Handle							instanceHandle = nil;	// Handle to instance globals
812	ComponentResult					result = noErr;			// Result return by this routine
813	IsochComponentGlobalsHandle		refConHdl = nil;
814	IsochComponentGlobalsPtr		gGlobals = nil;
815
816	// Check the ComponentRefcon to see if this 'Open' request is part of the
817	// registering process (Open, Register, Close) or a true 'Open'.
818	// By convention in FWD, the ComponentRefcon is used to convey Registration
819	// information and to point to a structure of items that are shared by component
820	// instances as shown below:
821	//
822	//			NULL
823	//			The component has NOT been successfully registered.
824	//
825	//			0xFFFFFFFF
826	//			The component has been successfully registered, but NO shared data
827	//			has been allocated or initialized.
828	//
829	//			Other Value
830	//			Any other value means that the component has been successfully
831	//			registered.  Additionally, it points to the data that is shared
832	//			between ComponentInstances.
833
834//���	RecordEventLogger( 'isoc', 'open', 0, 0);
835
836	SetComponentInstanceStorage(self, (Handle) 0);				// save instance ptr in component
837
838	refConHdl = (IsochComponentGlobalsHandle) GetComponentRefcon((Component) self);
839	if ( refConHdl == nil )					// Is this a register call?
840	{
841		RecordEventLogger( 'isoc', 'open', 'reg ', 0);
842		gCurInstance = self;						// Save for register
843		return(noErr);								// Just return lt register happen
844	}
845
846	//======================================================================
847	// Start allocating instance storage
848
849	ReserveMem( sizeof(IsochComponentInstance) );
850	instanceHandle = NewHandleClear( sizeof(IsochComponentInstance) );	// allocate instance storage
851	FailWithAction( instanceHandle == nil, result = MemError(), BadExit);
852
853	HLock((Handle) instanceHandle);
854	instancePtr = (IsochComponentInstancePtr) *instanceHandle;
855	instancePtr->instanceHandle = instanceHandle;
856	instancePtr->self = self;
857	SetComponentInstanceStorage(self, (Handle) instancePtr);				// save instance ptr in component
858
859	RecordEventLogger( 'isoc', 'open', 0, (unsigned long) instancePtr);
860
861Exit:
862	instancePtr->gGlobals		= *refConHdl;
863	gGlobals 					= *refConHdl;
864	gGlobals->lastInstance		= instancePtr;
865
866	// allocate queues for client read and write requests
867	result = PBQueueCreate( &instancePtr->readQueue);
868	FailWithVal( result != noErr, BadExit, result);
869
870	result = PBQueueInit( instancePtr->readQueue);
871	FailWithVal( result != noErr, BadExit, result);
872
873	result = PBQueueCreate( &instancePtr->writeQueue);
874	FailWithVal( result != noErr, BadExit, result);
875
876	result = PBQueueInit( instancePtr->writeQueue);
877	FailWithVal( result != noErr, BadExit, result);
878
879	FailMessage( result != noErr);
880
881	if (1 == CountComponentInstances((Component)self))		// Is this the first Open?
882	{
883		// Register all devices that are currently online.
884		UInt32 devIndex;
885		OSStatus status;
886		RecordEventLogger('isoc', '1st ', 'open', gGlobals->nDevices);
887
888		for (devIndex = 0; devIndex < gGlobals->nDevices; ++devIndex)
889		{
890			status = registerDevice(
891					&gGlobals->deviceDescription[devIndex].regEntryID,
892					&gGlobals->deviceDescription[devIndex].fwClientID,
893					&gGlobals->deviceDescription[devIndex]);
894
895			FailMessage(noErr != status);
896		}
897	}
898
899	return result;
900
901BadExit:
902
903	// chunk component stuff if last instance
904	if (CountComponentInstances((Component)self) == 1 && gGlobals != nil)
905	{
906		int i;
907
908		for( i=0; i<kMaxDevicesActive; ++i)
909		{
910			PBQueueDelete( gGlobals->deviceDescription[i].freeQueue);	// release queues
911			PBQueueDelete( gGlobals->deviceDescription[i].usedQueue);	// release queues
912		}
913
914		DisposeHandle( (Handle) refConHdl);
915		refConHdl = nil;
916		gGlobals = nil;
917
918		SetComponentRefcon((Component) self, (SInt32) 0xFFFFFFFF);
919	}
920
921	if( instanceHandle != nil)
922	{
923		DisposeHandle( instanceHandle);
924		instanceHandle = nil;
925	}
926
927	FailMessage( result != noErr);
928
929	return result;
930}
931
932//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
933// Registers the component. Returns non-zero result if component should not
934// be registered. This routine is called by the IDHOpen routine
935// after the instance globals are allocated, but before the component globals
936// are instantiated. This routine should not call any other functions that
937// require component globals or preferences.
938// See this mark for more information on variables handling: GlobalsAndPrefs.
939static pascal ComponentResult IDHRegister( IsochComponentInstancePtr globals )
940{
941#pragma unused( globals)
942
943	ComponentResult 			result = false;		// Assume success
944	int 						i;
945	IsochComponentGlobalsHandle refConHdl;
946	IsochComponentGlobals		*gGlobals;
947	IsochComponentInstance		*localInstance;
948
949	RecordEventLogger( 'isoc', 'reg ', 0, 0);
950
951	//=========================================================================
952	// Allocate globals for all component instances
953
954	ReserveMem(sizeof(IsochComponentGlobals));
955	refConHdl = (IsochComponentGlobalsHandle) NewHandleSysClear(sizeof(IsochComponentGlobals));	// Try and allocate refCon data
956	FailWithAction( refConHdl == nil, result = MemError(), Exit);
957
958	HLock( (Handle) refConHdl);
959	gGlobals = *refConHdl;
960
961	SetComponentRefcon((Component) gCurInstance, (long) refConHdl);
962
963	for( i=0; i<kMaxDevicesActive; ++i)	// initialize device decription structs
964	{
965		int j;
966
967		// create free and used queue for each device
968		result = PBQueueCreate( &gGlobals->deviceDescription[i].freeQueue);
969		FailWithVal( result != noErr, Exit, result);
970
971		result = PBQueueInit( gGlobals->deviceDescription[i].freeQueue);
972		FailWithVal( result != noErr, Exit, result);
973
974		result = PBQueueCreate( &gGlobals->deviceDescription[i].usedQueue);
975		FailWithVal( result != noErr, Exit, result);
976
977		result = PBQueueInit( gGlobals->deviceDescription[i].usedQueue);
978		FailWithVal( result != noErr, Exit, result);
979
980		// initialize other variables
981		gGlobals->deviceDescription[i].nQueueElements 				= 0;
982		gGlobals->deviceDescription[i].deviceID		 				= nil;
983		gGlobals->deviceDescription[i].readLocks 					= 0;
984		gGlobals->deviceDescription[i].writeLocks 					= 0;
985		gGlobals->deviceDescription[i].exclusiveAccess 				= 0;
986		gGlobals->deviceDescription[i].readIsochNotificationID 		= nil;
987		gGlobals->deviceDescription[i].writeIsochNotificationID 	= nil;
988
989		for( j=0; j<kMaxInstances; ++j)
990			gGlobals->deviceDescription[i].instancePtr[j] = nil;
991	}
992
993	// see if CMP should be turned on
994	gGlobals->useCMP = useCMP();
995
996	localInstance = (IsochComponentInstance *) NewPtrSysClear( sizeof( IsochComponentInstance));
997	FailWithAction( localInstance == NULL, result = MemError(), Exit);
998
999	localInstance->self 	= (ComponentInstance) localInstance;
1000	localInstance->gGlobals	= gGlobals;	// pointer to component globals
1001
1002	gGlobals->localInstance = localInstance;
1003
1004	gGlobals->seed 		= 1;
1005	gGlobals->nDevices	= 0;
1006
1007Exit:
1008	return result;
1009}
1010
1011//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1012// The component is being unregistered. Mark the globals to be disposed.
1013// See this mark for more information on variables handling: GlobalsAndPrefs.
1014static pascal ComponentResult IDHUnregister( IsochComponentInstancePtr iGlobals )
1015{
1016	IsochComponentGlobalsPtr	gGlobals;
1017
1018	RecordEventLogger( 'isoc', 'ureg', 0, 0);
1019
1020	FailIf(iGlobals == nil, Exit);
1021
1022	gGlobals = iGlobals->gGlobals;
1023	FailIf(gGlobals == nil, Exit);
1024
1025	gGlobals->unRegistered = true;
1026
1027Exit:
1028	return noErr;
1029}
1030
1031#pragma mark -
1032#pragma mark ���������� IDH Component Calls  ����������
1033
1034pascal ComponentResult	IDHCancelNotification( ComponentInstance ihc, IDHNotificationID	notificationID)
1035{
1036	OSErr 						result = noErr;
1037	ClientNotification*			clientNotification = (ClientNotification*)notificationID;
1038
1039	RecordEventLogger( 'isoc', 'canc', 'el  ', 'noti');
1040
1041	FailWithAction( clientNotification == nil, result = paramErr, Exit);
1042
1043	clientNotification->events = 0;
1044
1045Exit:
1046	return result;
1047}
1048
1049pascal ComponentResult IDHCancelPendingIO( ComponentInstance ihc, IDHParameterBlock *pb)
1050{
1051	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
1052	OSErr 						result = noErr;
1053	QHdrPtr						queue = nil;
1054
1055	RecordEventLogger( 'isoc', 'canc', 'el  ', 'pend');
1056
1057	FailWithAction( pb == nil, result = paramErr, Exit);
1058
1059	if( ih->permissions & kIDHOpenForReadTransactions)
1060		queue = ih->readQueue;
1061
1062	if( ih->permissions & kIDHOpenForWriteTransactions)
1063		queue = ih->writeQueue;
1064
1065	if( queue)
1066	{
1067		result = PBDequeue( (QElem *) pb, queue);
1068//		FailWithVal( result != noErr, Exit, result);
1069		if( result != noErr)
1070			goto Exit;
1071	}
1072
1073Exit:
1074	return result;
1075
1076}
1077
1078pascal ComponentResult	IDHCloseDevice(	ComponentInstance ihc)
1079{
1080	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
1081	OSErr 						result = noErr;
1082	IsochComponentGlobalsPtr	gGlobals = ih->gGlobals;
1083	DeviceDescription			*deviceDescriptionPtr = nil;
1084	short						i;
1085	IDHParameterBlock 			*IORequest;
1086
1087	RecordEventLogger( 'isoc', 'clos', ' dev', 0);
1088
1089	FailWithAction( ih->deviceID == nil, result = kIDHErrDeviceNotConfigured, Exit);
1090
1091	if( ih->permissions == 0)	// client has no open devices?
1092		goto Exit;
1093
1094	// flush IO queues
1095	while( PBDequeueFirst( ih->readQueue, (QElemPtr *) &IORequest) == noErr) ;
1096	while( PBDequeueFirst( ih->writeQueue, (QElemPtr *) &IORequest) == noErr) ;
1097
1098	result = findDeviceDescriptionforDevice( ih, ih->deviceID, &deviceDescriptionPtr);	// find the device
1099	FailWithVal( result != noErr, Exit, result);
1100
1101	for( i=0; i<kMaxInstances; ++i) // remove instance from device list
1102	{
1103		if( deviceDescriptionPtr->instancePtr[i] == ih)
1104		{
1105			deviceDescriptionPtr->instancePtr[i] = nil;
1106		}
1107	}
1108
1109	FailWithAction( deviceDescriptionPtr->deviceID == nil, result = kIDHErrDeviceNotOpened, Exit);
1110
1111	if( ih->permissions & kIDHOpenForReadTransactions)	// tear down read
1112	{
1113		if( --deviceDescriptionPtr->readLocks <= 0)	// is device completely freed by clients?
1114		{
1115			if( deviceDescriptionPtr->readIsochNotificationID)
1116			{
1117				result = IDHCancelNotification( (ComponentInstance) gGlobals->localInstance,
1118							deviceDescriptionPtr->readIsochNotificationID);
1119				FailMessage( result != noErr);
1120
1121				result = IDHDisposeNotification( (ComponentInstance) gGlobals->localInstance,
1122							deviceDescriptionPtr->readIsochNotificationID);
1123				FailMessage( result != noErr);
1124
1125				deviceDescriptionPtr->readIsochNotificationID = nil;
1126			}
1127
1128			result = disableRead(ih);
1129			FailMessage( result != noErr);
1130
1131			deviceDescriptionPtr->exclusiveAccess = false;
1132		}
1133	}
1134
1135	if( ih->permissions & kIDHOpenForWriteTransactions) // tear down write
1136	{
1137		if( --deviceDescriptionPtr->writeLocks <= 0)	// is device completely freed by clients?
1138		{
1139			if( deviceDescriptionPtr->writeIsochNotificationID)
1140			{
1141				result = IDHCancelNotification( (ComponentInstance) gGlobals->localInstance,
1142							deviceDescriptionPtr->writeIsochNotificationID);
1143				FailMessage( result != noErr);
1144
1145				result = IDHDisposeNotification( (ComponentInstance) gGlobals->localInstance,
1146							deviceDescriptionPtr->writeIsochNotificationID);
1147				FailMessage( result != noErr);
1148
1149				deviceDescriptionPtr->writeIsochNotificationID = nil;
1150			}
1151
1152			result = disableWrite(ih);
1153			FailMessage( result != noErr);
1154
1155			deviceDescriptionPtr->exclusiveAccess = false;
1156		}
1157	}
1158
1159	result = postEvent( ih->gGlobals, ih->deviceID,
1160		(ih->permissions & kIDHOpenForReadTransactions)?kIDHEventReadDisabled:kIDHEventWriteDisabled, nil);
1161	FailWithVal( result != noErr, Exit, result);
1162
1163Exit:
1164	ih->permissions = 0;	// make sure device is closed
1165
1166	return result;
1167}
1168
1169pascal ComponentResult	IDHDisposeNotification(
1170		ComponentInstance ihc,
1171		IDHNotificationID notificationID)
1172{
1173	OSErr 					result = noErr;
1174	ClientNotification*		clientNotification = (ClientNotification*)notificationID;
1175
1176	RecordEventLogger( 'isoc', 'disp', 'ose ', 'noti');
1177
1178	FailWithAction( clientNotification == nil, result = paramErr, Exit);
1179
1180	clientNotification->deviceID = 0;
1181
1182Exit:
1183	return result;
1184}
1185
1186pascal ComponentResult	IDHGetDeviceClock(	ComponentInstance 	ihc,
1187											Component			*clock )
1188{
1189	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
1190	DeviceDescription			*deviceDescriptionPtr;
1191	OSErr 						result = noErr;
1192
1193	RecordEventLogger( 'isoc', 'get ', 'clok', 0);
1194
1195	FailWithAction( clock == nil, result = paramErr, Exit);
1196
1197	FailWithAction( ih->deviceID == nil, result = kIDHErrDeviceNotConfigured, Exit);
1198
1199	result = findDeviceDescriptionforDevice( ih, ih->deviceID, &deviceDescriptionPtr);
1200	FailWithVal( result != noErr, Exit, result);
1201
1202//	FailWithAction( deviceDescriptionPtr->active == false, result = kIDHErrDeviceDisconnected, Exit);
1203
1204	*clock = deviceDescriptionPtr->clock;
1205
1206Exit:
1207	return result;
1208}
1209
1210//��� USER BETTER KEEP HIS DEVICE LIST AROUND IF THIS IS TO MEAN ANYTHING
1211pascal ComponentResult	IDHGetDeviceConfiguration( 	ComponentInstance 	ihc,
1212													QTAtomSpec			*configID )
1213{
1214	IsochComponentInstancePtr	ih = (IsochComponentInstancePtr) ihc;
1215	OSErr 						result = noErr;
1216
1217	RecordEventLogger( 'isoc', 'get ', 'dev ', 'conf');
1218
1219	FailWithAction( configID == nil, result = paramErr, Exit);
1220	FailWithAction( ih->currentConfig.container == nil || ih->currentConfig.atom == nil,
1221		result = kIDHErrDeviceNotConfigured, Exit);
1222
1223	*configID = ih->currentConfig;
1224
1225Exit:
1226	return result;
1227}
1228
1229pascal ComponentResult IDHGetDeviceControl(ComponentInstance ihc, ComponentInstance *deviceControl)
1230{
1231	ComponentResult				result = noErr;
1232	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
1233	DeviceDescription			*deviceDescriptionPtr;
1234
1235	RecordEventLogger( 'isoc', 'get ', 'dev ', 'ctrl');
1236
1237	if ( deviceControl == nil )
1238		return(paramErr);
1239
1240	FailWithAction( ih->deviceID == nil, result = kIDHErrDeviceNotConfigured, Exit);
1241
1242	result = findDeviceDescriptionforDevice( ih, ih->deviceID, &deviceDescriptionPtr);
1243	FailWithVal( result != noErr, Exit, result);
1244
1245	FailWithAction( deviceDescriptionPtr->active == false, result = kIDHErrDeviceDisconnected, Exit);
1246
1247	*deviceControl = deviceDescriptionPtr->deviceControlInstance;
1248
1249	FailMessageVal( *deviceControl == nil, (long) ih);
1250
1251Exit:
1252	return(result);
1253}
1254
1255//��� USER MUST FREE ATOM LIST WHEN FINISHED
1256pascal ComponentResult	IDHGetDeviceList(	ComponentInstance 	ihc,
1257											QTAtomContainer		*deviceList )
1258
1259{
1260	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
1261	IsochComponentGlobalsPtr	gGlobals = ih->gGlobals;
1262	OSErr 						result = noErr;
1263	QTAtomContainer				container = nil;
1264	short						devIndex;
1265	UInt32						version;
1266
1267	RecordEventLogger( 'isoc', 'get ', 'dev ', 'list');
1268
1269	FailWithAction( deviceList == nil, result = paramErr, Exit);
1270
1271	// create device atom list now
1272	result = QTNewAtomContainer( &container);
1273	FailWithVal( result != noErr, Exit, result);
1274
1275	// save seed
1276	result = QTInsertChild( container, kParentAtomIsContainer, kIDHSeedAtomType, 0, 0,
1277				sizeof( gGlobals->seed), &gGlobals->seed, nil);
1278	FailWithVal( result != noErr, Exit, result);
1279
1280	version = (DVVersion << 24) | (DVRevision << 16) | DVBuildNumber;
1281
1282	// save isoch version
1283	result = QTInsertChild( container, kParentAtomIsContainer, kIDHIsochVersionAtomType, 0, 0,
1284				sizeof( UInt32), &version, nil);
1285	FailWithVal( result != noErr, Exit, result);
1286
1287	// save useCMP value
1288	result = QTInsertChild( container, kParentAtomIsContainer, kIDHUseCMPAtomType, 0, 0,
1289				sizeof( UInt32), &gGlobals->useCMP, nil);
1290	FailWithVal( result != noErr, Exit, result);
1291
1292	// save each active device
1293	for( devIndex=0; devIndex<kMaxDevicesActive; ++devIndex)
1294	{
1295		if( gGlobals->deviceDescription[devIndex].deviceID != 0 && gGlobals->deviceDescription[devIndex].active)
1296		{
1297			result = QTInsertChildren( container, kParentAtomIsContainer, gGlobals->deviceDescription[devIndex].deviceContainer);
1298			FailWithVal( result != noErr, Exit, result);
1299		}
1300	}
1301
1302	*deviceList = container;
1303
1304Exit:
1305	if( result != noErr && container)
1306	{
1307		QTRemoveChildren( container, kParentAtomIsContainer);
1308		QTDisposeAtomContainer( container);
1309		container = nil;
1310	}
1311
1312	return result;
1313}
1314
1315pascal ComponentResult	IDHGetDeviceStatus(	ComponentInstance 	ihc,
1316											const QTAtomSpec	*devSpec,
1317											IDHDeviceStatus		*status )
1318{
1319	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
1320	IsochComponentGlobalsPtr	gGlobals = ih->gGlobals;
1321	OSErr 						result = noErr;
1322	IDHDeviceID					deviceID = nil;
1323	QTAtom						deviceInfoAtom, deviceAtom;
1324	QTAtomSpec					volatileAtomSpec;
1325	long						size;
1326	DeviceDescription			*deviceDescriptionPtr;
1327
1328	RecordEventLogger( 'isoc', 'get ', 'stat', 0);
1329
1330	FailWithAction( devSpec == nil, result = paramErr, Exit);
1331	FailWithAction( status == nil, result = paramErr, Exit);
1332
1333	volatileAtomSpec = *devSpec;
1334
1335	result = checkSeed( ih->gGlobals, &volatileAtomSpec);
1336	if( result != noErr)
1337		goto Exit;
1338
1339	result = getDeviceID( &volatileAtomSpec, &deviceID);
1340	FailWithVal( result != noErr, Exit, result);
1341
1342	result = findDeviceDescriptionforDevice( ih, deviceID, &deviceDescriptionPtr);	// find description for this device
1343	FailWithVal( result != noErr, Exit, result);
1344
1345	deviceAtom = QTFindChildByIndex( deviceDescriptionPtr->deviceContainer, kParentAtomIsContainer, kIDHDeviceAtomType, 1, nil);
1346	FailWithAction( deviceAtom == nil, result = kIDHErrDeviceList, Exit);
1347
1348	// find device status for this device
1349	deviceInfoAtom = QTFindChildByIndex( deviceDescriptionPtr->deviceContainer, deviceAtom, kDVDeviceInfo, 1, nil);
1350	FailWithAction( deviceInfoAtom == nil, result = kIDHErrDeviceList, Exit);
1351
1352	QTLockContainer( deviceDescriptionPtr->deviceContainer);
1353
1354	// get the value of the mediaType atom
1355	QTCopyAtomDataToPtr( deviceDescriptionPtr->deviceContainer, deviceInfoAtom, true, sizeof( IDHDeviceStatus),
1356		status, &size);
1357
1358	QTUnlockContainer( deviceDescriptionPtr->deviceContainer);
1359
1360	status->version = 				100;
1361	status->physicallyConnected =	true;
1362	status->readEnabled = 			deviceDescriptionPtr->readLocks;
1363	status->writeEnabled = 			deviceDescriptionPtr->writeLocks;
1364	status->exclusiveAccess = 		deviceDescriptionPtr->exclusiveAccess;
1365	status->currentBandwidth = 		0;
1366	status->currentChannel = 		0;
1367
1368	//��� need to make this work with camera tracking
1369	status->deviceActive = 			deviceDescriptionPtr->active;
1370	status->inputStandard =			deviceDescriptionPtr->standard;
1371// JKL *** what to to with this? does this mean deviceID, cameraFWClientID, or localNodeFWClientID
1372// Think this is for clock to set the localFWReferenceID
1373	status->localNodeID	= 			(PsuedoID) deviceDescriptionPtr->localFWReferenceID;
1374
1375	result = QTSetAtomData( deviceDescriptionPtr->deviceContainer, deviceInfoAtom, sizeof( IDHDeviceStatus), status);
1376	FailWithVal( result != noErr, Exit, result);
1377
1378Exit:
1379	return result;
1380}
1381
1382pascal ComponentResult	IDHNewNotification(	ComponentInstance 	ihc,
1383											IDHDeviceID			deviceID,
1384											IDHNotificationProc	notificationProc,
1385											void				*userData,
1386											IDHNotificationID 	*notificationID)
1387{
1388	IsochComponentGlobalsPtr 	gGlobals = ((IsochComponentInstancePtr)ihc)->gGlobals;
1389	UInt32						i;
1390	Boolean						addedClient = false;
1391	OSErr 						result = noErr;
1392
1393	RecordEventLogger( 'isoc', 'new ', 'noti', 'fy  ');
1394
1395	FailWithAction( notificationProc == nil, result = paramErr, Exit);
1396	FailWithAction( notificationID == nil, result = paramErr, Exit);
1397
1398	i = 0;
1399	while (i < kMaxNotifications)
1400	{
1401		if (0 == gGlobals->clientNotification[i].deviceID)
1402		{
1403			gGlobals->clientNotification[i].ihc = ihc;
1404			gGlobals->clientNotification[i].deviceID = deviceID;
1405			gGlobals->clientNotification[i].notificationProc = notificationProc;
1406			gGlobals->clientNotification[i].events = 0;
1407			gGlobals->clientNotification[i].userData = userData;
1408			*notificationID = (UInt32)&gGlobals->clientNotification[i];
1409			addedClient = true;
1410			break;
1411		}
1412
1413		++i;
1414	}
1415
1416	if (!addedClient)				// List is full. Unable to add addtional clients
1417		result = paramErr;
1418
1419Exit:
1420	return result;
1421}
1422
1423pascal ComponentResult	IDHNotifyMeWhen(	ComponentInstance 	ihc,
1424											IDHNotificationID	notificationID,
1425											IDHEvent			events )
1426{
1427	OSErr 						result = noErr;
1428	ClientNotification*			clientNotification = (ClientNotification*)notificationID;
1429
1430	RecordEventLogger( 'isoc', 'noti', 'when', events);
1431
1432	FailWithAction( clientNotification == nil, result = paramErr, Exit);
1433
1434	clientNotification->events = events;
1435
1436Exit:
1437	return result;
1438}
1439
1440pascal ComponentResult	IDHOpenDevice( ComponentInstance ihc, UInt32 permissions)
1441{
1442	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
1443	IsochComponentGlobalsPtr	gGlobals = ih->gGlobals;
1444	OSErr 						result = noErr;
1445	short						i;
1446	DeviceDescription			*deviceDescriptionPtr = nil;
1447	Component					devcComp = 0;
1448	ComponentInstance			devc = 0;
1449
1450	RecordEventLogger( 'isoc', 'open', ' dev', 0);
1451
1452	FailWithAction( permissions == 0, result = paramErr, Exit);
1453
1454	FailWithAction( ih->deviceID == nil, result = kIDHErrDeviceNotConfigured, Exit);
1455
1456	result = findDeviceDescriptionforDevice( ih, ih->deviceID, &deviceDescriptionPtr);
1457	FailWithVal( result != noErr, Exit, result);
1458
1459	FailWithAction( deviceDescriptionPtr->active == false, result = kIDHErrDeviceDisconnected, Exit);
1460
1461	// trying to reopen for read or write?
1462	if( ih->permissions & kIDHOpenForReadTransactions && permissions & kIDHOpenForReadTransactions)
1463		goto Exit;	// don't do anything
1464
1465	if( ih->permissions & kIDHOpenForWriteTransactions && permissions & kIDHOpenForWriteTransactions)
1466		goto Exit;	// don't do anything
1467
1468	// check for switching directions before opening
1469	FailWithAction( ih->permissions & kIDHOpenForReadTransactions && permissions & kIDHOpenForWriteTransactions,
1470		result = kIDHErrDeviceInUse, Exit); //���
1471
1472	FailWithAction( ih->permissions & kIDHOpenForWriteTransactions && permissions & kIDHOpenForReadTransactions,
1473		result = kIDHErrDeviceInUse, Exit); //���
1474
1475	// make sure device wasn't already opened for exclusive access
1476	FailWithAction( deviceDescriptionPtr->exclusiveAccess == true, result = kIDHErrDeviceInUse, Exit);
1477
1478	if( permissions & kIDHOpenForReadTransactions)	// add buffers to queue for read devices
1479	{
1480		result = addBuffersToQueue( deviceDescriptionPtr);
1481		FailWithVal( result != noErr, Exit, result);
1482	}
1483
1484	// if user is opening for read, make sure device isn't already opened for writes
1485	if( permissions & kIDHOpenForReadTransactions)
1486	{
1487		FailWithAction( deviceDescriptionPtr->writeLocks, result = kIDHErrDeviceInUse, Exit);
1488
1489		if( deviceDescriptionPtr->readLocks == 0)
1490		{
1491			result = enableRead(ih);
1492			FailWithVal( result != noErr, Exit, result);
1493		}
1494
1495		++deviceDescriptionPtr->readLocks;	// keep track of read count
1496	}
1497
1498	// if user is opening for write, make sure device isn't already opened
1499	if( permissions & kIDHOpenForWriteTransactions)
1500	{
1501		FailWithAction( deviceDescriptionPtr->readLocks || deviceDescriptionPtr->writeLocks, result = kIDHErrDeviceInUse, Exit);
1502
1503		result = enableWrite(ih);
1504		FailWithVal( result != noErr, Exit, result);
1505
1506		deviceDescriptionPtr->writeLocks = 1;	// keep track of write count
1507	}
1508
1509	if( permissions & kIDHOpenWithExclusiveAccess)
1510		deviceDescriptionPtr->exclusiveAccess = true;
1511
1512	ih->permissions = permissions;	// save the permissions
1513
1514	// see if this client has already been registered for this device
1515	for( i=0; i<kMaxInstances; ++i)
1516		if( deviceDescriptionPtr->instancePtr[i] == ih)
1517			break;
1518
1519	if( i >= kMaxInstances)	// client not registered?
1520	{
1521		for( i=0; i<kMaxInstances; ++i)
1522		{
1523			if( deviceDescriptionPtr->instancePtr[i] == nil) // find an empty slot, and add client
1524			{
1525				deviceDescriptionPtr->instancePtr[i] = ih;
1526				break;
1527			}
1528		}
1529
1530		FailWithAction( i >= kMaxInstances, result = kIDHErrInvalidIndex, Exit);	// no slots available
1531	}
1532
1533	result = postEvent( ih->gGlobals, ih->deviceID,
1534		(permissions & kIDHOpenForReadTransactions)?kIDHEventReadEnabled:kIDHEventWriteEnabled, nil);
1535	FailWithVal( result != noErr, Exit, result);
1536
1537Exit:
1538	return result;
1539}
1540
1541// can be called synchronously at task level only
1542pascal ComponentResult	IDHRead( ComponentInstance ihc, IDHParameterBlock *pb)
1543{
1544	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
1545	OSErr 						result = noErr;
1546	DeviceDescription			*deviceDescriptionPtr;
1547	Boolean						synchronous = false;
1548	IsochComponentGlobalsPtr	gGlobals = ih->gGlobals;
1549
1550	RecordEventLogger( 'isoc', 'read', (unsigned long) ih->readQueue, (unsigned long) pb);
1551
1552	FailWithAction( pb == nil, result = paramErr, Exit);
1553
1554	// can't do sync reads at non-task level
1555	FailWithAction( pb->completionProc == nil && CurrentExecutionLevel() != kTaskLevel, result = paramErr, Exit);
1556
1557	FailWithAction( ih->deviceID == nil, result = kIDHErrDeviceNotConfigured, Exit);
1558
1559	result = findDeviceDescriptionforDevice( ih, ih->deviceID, &deviceDescriptionPtr);
1560	FailWithVal( result != noErr, Exit, result);
1561
1562	FailWithAction( deviceDescriptionPtr->active == false, result = kIDHErrDeviceDisconnected, Exit);
1563
1564	if( deviceDescriptionPtr->readIsochNotificationID == nil)
1565	{
1566		result = IDHNewNotification( (ComponentInstance) gGlobals->localInstance, ih->deviceID, readNotifyProc,
1567				(void *) ih->gGlobals, &deviceDescriptionPtr->readIsochNotificationID);
1568		FailWithVal( result != noErr, Exit, result);
1569	}
1570
1571	if ( deviceDescriptionPtr->readIsochNotificationID )
1572	{
1573		result = IDHNotifyMeWhen( (ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr->readIsochNotificationID,
1574				kIDHPrivateEventReadComplete);
1575		FailWithVal( result != noErr, Exit, result);
1576	}
1577
1578	if( pb->completionProc == nil) // synchronous read
1579	{
1580		synchronous = true;
1581
1582		pb->completionProc = localCompletionRoutine;
1583		pb->refCon 			= deviceDescriptionPtr;
1584
1585		deviceDescriptionPtr->complete = false;
1586	}
1587
1588	PBEnqueueLast( (QElemPtr) pb, ih->readQueue);	// queue the read
1589
1590	if( synchronous) // synchronous read
1591	{
1592		AbsoluteTime 	expirationTime;
1593		TimerID			theTimer;
1594
1595		ih->timeout = false;
1596
1597		// expiration is current time + 1/15 second (2 frame times)
1598		expirationTime = AddDurationToAbsolute( kTimeoutDuration, UpTime());
1599
1600		result = SetInterruptTimer( &expirationTime, myTimerHandler, ih, &theTimer);
1601		FailWithVal( result != noErr, Exit, result);
1602
1603		while( deviceDescriptionPtr->complete == false && ih->timeout == false)	// loop until complete or timeout
1604			;
1605
1606		expirationTime = DurationToAbsolute( 0);
1607		CancelTimer( theTimer, &expirationTime);
1608
1609		pb->completionProc	= nil;
1610
1611		FailWithAction( ih->timeout == true, result = kIDHErrDeviceTimeout, Exit); //��� correct error code
1612	}
1613
1614Exit:
1615
1616	return result;
1617}
1618
1619pascal ComponentResult	IDHReleaseBuffer( ComponentInstance ihc, IDHParameterBlock *pb)
1620{
1621	OSErr 						result = noErr;
1622	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
1623	DeviceDescription			*deviceDescriptionPtr;
1624	QueueElement				*qep;
1625
1626	RecordEventLogger( 'isoc', 'rele', 'ase ', 'buff');
1627
1628	FailWithAction( pb == nil, result = paramErr, Exit);
1629
1630	FailWithAction( ih->deviceID == nil, result = kIDHErrDeviceNotConfigured, Exit);
1631
1632	result = findDeviceDescriptionforDevice( ih, ih->deviceID, &deviceDescriptionPtr);
1633	FailWithVal( result != noErr, Exit, result);
1634
1635	qep = (QueueElement *) deviceDescriptionPtr->usedQueue->qHead;
1636	while( qep)	// find this buffer in the list of used buffers
1637	{
1638		if( qep->buffer == pb->buffer)	// is this our buffer?
1639		{
1640			if( --qep->locks <= 0)	// are all of the locks gone?
1641			{
1642				qep->locks = 0;
1643
1644				// remove from used queue
1645				PBDequeue( (QElemPtr) qep, deviceDescriptionPtr->usedQueue);
1646
1647				// enqueue this list in the free queue
1648				PBEnqueueLast( (QElemPtr) qep, deviceDescriptionPtr->freeQueue);
1649			}
1650
1651			break;
1652		}
1653
1654		// check next element in the used queue
1655		qep = (QueueElement *) qep->qLink;
1656	}
1657
1658Exit:
1659	return result;
1660}
1661
1662pascal ComponentResult	IDHSetDeviceConfiguration( 	ComponentInstance 	ihc,
1663													const QTAtomSpec	*configID)
1664{
1665	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
1666	OSErr 						result = noErr;
1667	IsochComponentGlobalsPtr	gGlobals = ih->gGlobals;
1668	QTAtomSpec					volatileAtomSpec;
1669	IDHDeviceID					previousDeviceID;
1670	DeviceDescription			*deviceDescriptionPtr;
1671
1672	RecordEventLogger( 'isoc', 'set ', 'dev ', 'conf');
1673
1674	FailWithAction( configID == nil, result = paramErr, Exit);
1675
1676	FailWithAction( configID->container == nil, result = paramErr, Exit);
1677
1678	// if the client is setting to the same config, then we are ok
1679	if( configID->container == ih->currentConfig.container &&
1680		configID->atom		== ih->currentConfig.atom)
1681		goto Exit;
1682
1683	// device already in use, please close device first
1684	FailWithAction( ih->permissions != 0, result = kIDHErrDeviceInUse, Exit);
1685
1686	previousDeviceID = ih->deviceID;
1687
1688	volatileAtomSpec = *configID;
1689	result = getDeviceID( &volatileAtomSpec, &ih->deviceID);
1690	FailWithVal( result != noErr, Exit, result);
1691
1692	result = checkSeed( ih->gGlobals, &volatileAtomSpec);
1693	FailWithVal( result != noErr, Exit, result);
1694
1695	ih->currentConfig = *configID;
1696
1697	result = findDeviceDescriptionforDevice( ih, ih->deviceID, &deviceDescriptionPtr);
1698	FailWithVal( result != noErr, Exit, result);
1699
1700	FailWithAction( deviceDescriptionPtr->active == false, result = kIDHErrDeviceDisconnected, Exit);
1701
1702	// if this client already has device control, free the previous connection
1703	if( previousDeviceID && ih->hasDeviceControl)
1704	{
1705		DeviceDescription *deviceDescriptionPtr2;
1706
1707		result = findDeviceDescriptionforDevice( ih, previousDeviceID, &deviceDescriptionPtr2);
1708		FailWithVal( result != noErr, Exit, result);
1709
1710		result = closeDeviceControl( ihc, deviceDescriptionPtr2);
1711		FailMessage( result != noErr);
1712
1713		ih->hasDeviceControl = false;
1714	}
1715
1716	// =======================================================================
1717	// All should be good here from a device stand point, now open the device
1718	// control component.  If the devc is not nil either we didn't release it
1719	// from close or the same client is calling open again.
1720
1721	if ( deviceDescriptionPtr->deviceControlInstance == nil)
1722	{
1723		ComponentDescription	devcDesc;
1724		Component				devcComp;
1725		ComponentInstance		devc;
1726
1727		devcDesc.componentType 			= kDeviceControlComponentType;
1728		devcDesc.componentSubType 		= kDeviceControlSubtypeFWDV;
1729		devcDesc.componentManufacturer 	= 0;
1730		devcDesc.componentFlags 		= 0L;
1731		devcDesc.componentFlagsMask 	= 0L;
1732
1733		devcComp = FindNextComponent( devcComp, &devcDesc);
1734		FailMessage( devcComp == nil);
1735
1736		if ( devcComp )
1737		{
1738			result = OpenAComponent(devcComp, &devc);
1739			FailWithVal( result != noErr, Exit, result);
1740
1741			result = DeviceControlSetDeviceConnectionID(devc, (DeviceConnectionID) deviceDescriptionPtr->fwClientID); //��� deviceID);
1742			FailWithVal( result != noErr, Exit, result);
1743
1744			result = DeviceControlEnableAVCTransactions(devc);
1745			FailWithVal( result != noErr, Exit, result);
1746
1747			deviceDescriptionPtr->deviceControlInstance = devc;
1748
1749			// now sign up for callback in case device is added or removed and we need to update connectionID
1750			if( deviceDescriptionPtr->deviceControlNotificationID == nil)
1751			{
1752				result = IDHNewNotification( (ComponentInstance) gGlobals->localInstance, ih->deviceID, deviceControlCallback, deviceDescriptionPtr,
1753							&deviceDescriptionPtr->deviceControlNotificationID);
1754				FailWithVal( result != noErr, Exit, result);
1755			}
1756
1757			if ( deviceDescriptionPtr->deviceControlNotificationID )
1758			{
1759				result = IDHNotifyMeWhen( (ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr->deviceControlNotificationID,
1760							kIDHEventDeviceAdded | kIDHEventDeviceRemoved);
1761				FailWithVal( result != noErr, Exit, result);
1762			}
1763		}
1764	}
1765
1766	if( ih->hasDeviceControl == false && deviceDescriptionPtr->deviceControlInstance != nil)
1767	{
1768		ih->hasDeviceControl = true;
1769		++deviceDescriptionPtr->deviceControlCount;
1770	}
1771
1772Exit:
1773	return result;
1774}
1775
1776pascal ComponentResult	IDHUpdateDeviceList(	ComponentInstance 	ihc,
1777												QTAtomContainer		*deviceList )
1778
1779{
1780	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
1781	IsochComponentGlobalsPtr	gGlobals = ih->gGlobals;
1782	OSErr 						result = noErr;
1783	QTAtomContainer				container = nil;
1784	short						nDVDevices, i;
1785	QTAtomSpec					atomSpec;
1786
1787	RecordEventLogger( 'isoc', 'updt', 'dev ', 'list');
1788
1789	FailWithAction( deviceList == nil, result = paramErr, Exit);
1790
1791	atomSpec.container = *deviceList;
1792
1793	result = checkSeed( gGlobals, &atomSpec);	// make sure the container is current
1794	if( result != noErr)
1795		goto Exit;
1796
1797	// check for useCMP value changing
1798	if (result == noErr)
1799	{
1800		QTAtom				useCMPAtom;
1801		UInt32				useCMPValue;
1802
1803		useCMPAtom = QTFindChildByIndex(*deviceList, kParentAtomIsContainer, kIDHUseCMPAtomType, 1, nil);
1804		if (useCMPAtom)
1805		{
1806			QTLockContainer(*deviceList);
1807			result = QTCopyAtomDataToPtr(*deviceList, useCMPAtom, true, 4, &useCMPValue, nil);
1808			QTUnlockContainer(*deviceList);
1809
1810			if (result == noErr)
1811				gGlobals->useCMP = useCMPValue;
1812
1813		}
1814	}
1815
1816	// move all volatile atoms here
1817	nDVDevices = QTCountChildrenOfType( *deviceList, kParentAtomIsContainer, kIDHDeviceAtomType);
1818	for( i=0; i<nDVDevices; ++i)
1819	{
1820		QTAtom 				deviceAtomNew, nameAtomNew, deviceIDAtom;
1821		QTAtom 				deviceAtomOld, nameAtomOld;
1822		DeviceDescription	*deviceDescriptionPtr;
1823		IDHDeviceID			deviceID;
1824		UInt8				newName[256];
1825		SInt32				actualSize;
1826
1827		// get the client supplied atoms
1828		deviceAtomNew = QTFindChildByIndex( *deviceList, kParentAtomIsContainer, kIDHDeviceAtomType, i + 1, nil);
1829		FailIf( deviceAtomNew == nil, Exit);
1830
1831		nameAtomNew = QTFindChildByIndex( *deviceList, deviceAtomNew, kIDHNameAtomType, 1, nil);
1832		FailIf( nameAtomNew == nil, Exit);
1833
1834		deviceIDAtom = QTFindChildByIndex( *deviceList, deviceAtomNew, kIDHDeviceIDType, 1, nil);
1835		FailIf( deviceIDAtom == nil, Exit);
1836
1837		QTLockContainer( *deviceList);
1838
1839		QTCopyAtomDataToPtr( *deviceList, deviceIDAtom, true, sizeof( IDHDeviceID), &deviceID, nil);
1840
1841		QTUnlockContainer( *deviceList);
1842
1843		// find the local copy of this device container
1844		result = findDeviceDescriptionforDevice( ih, deviceID, &deviceDescriptionPtr);
1845		FailWithVal( result != noErr, Exit, result);
1846
1847		deviceAtomOld = QTFindChildByIndex( deviceDescriptionPtr->deviceContainer, kParentAtomIsContainer, kIDHDeviceAtomType, 1, nil);
1848		FailIf( deviceAtomOld == nil, Exit);
1849
1850		nameAtomOld = QTFindChildByIndex( deviceDescriptionPtr->deviceContainer, deviceAtomOld, kIDHNameAtomType, 1, nil);
1851		FailIf( nameAtomOld == nil, Exit);
1852
1853		// get new name
1854		QTLockContainer( *deviceList);
1855		result = QTCopyAtomDataToPtr(*deviceList, nameAtomNew, true, 256, newName, &actualSize);
1856		QTUnlockContainer( *deviceList);
1857		FailWithVal( result != noErr, Exit, result);
1858
1859		// update prefs file with new name
1860		// JKL, don't want to do this for now since FCP might be using this routine to toggle CMP
1861//		updateCameraName(&deviceDescriptionPtr->uniqueID, newName);
1862
1863		// copy the new data into the current atom
1864		result = QTReplaceAtom( deviceDescriptionPtr->deviceContainer, nameAtomOld, *deviceList, nameAtomNew);
1865		FailWithVal( result != noErr, Exit, result);
1866	}
1867
1868Exit:
1869	return result;
1870}
1871
1872// can be called synchronously at task level only
1873pascal ComponentResult	IDHWrite( ComponentInstance ihc, IDHParameterBlock *pb)
1874{
1875	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
1876	OSErr 						result = noErr;
1877	DeviceDescription			*deviceDescriptionPtr;
1878	Boolean						synchronous = false;
1879	IsochComponentGlobalsPtr	gGlobals = ih->gGlobals;
1880
1881	RecordEventLogger( 'isoc', 'writ', 0, 0);
1882
1883	FailWithAction( pb == nil, result = paramErr, Exit);
1884
1885	// can't do sync writes at non-task level
1886	FailWithAction( pb->completionProc == nil && CurrentExecutionLevel() != kTaskLevel, result = paramErr, Exit);
1887
1888	// check for illegal condition
1889	FailWithAction( pb->completionProc == nil && pb->buffer == nil, result = paramErr, Exit);
1890
1891	FailWithAction( ih->deviceID == nil, result = kIDHErrDeviceNotConfigured, Exit);
1892
1893	result = findDeviceDescriptionforDevice( ih, ih->deviceID, &deviceDescriptionPtr);
1894	FailWithVal( result != noErr, Exit, result);
1895
1896	FailWithAction( deviceDescriptionPtr->active == false, result = kIDHErrDeviceDisconnected, Exit);
1897
1898	if( deviceDescriptionPtr->writeIsochNotificationID == nil)
1899	{
1900		result = IDHNewNotification( (ComponentInstance) gGlobals->localInstance, ih->deviceID, writeNotifyProc,
1901				(void *) ih->gGlobals, &deviceDescriptionPtr->writeIsochNotificationID );
1902		FailWithVal( result != noErr, Exit, result);
1903	}
1904
1905	if ( deviceDescriptionPtr->writeIsochNotificationID )
1906	{
1907		result = IDHNotifyMeWhen( (ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr->writeIsochNotificationID,
1908				kIDHPrivateEventWriteComplete);
1909		FailWithVal( result != noErr, Exit, result);
1910	}
1911
1912	if( pb->completionProc == nil) // synchronous write
1913	{
1914		synchronous = true;
1915
1916		pb->completionProc 	= localCompletionRoutine;
1917		pb->refCon 			= deviceDescriptionPtr;
1918
1919		deviceDescriptionPtr->complete = false;
1920	}
1921
1922	PBEnqueueLast( (QElemPtr) pb, ih->writeQueue);
1923
1924	if( synchronous) // synchronous write
1925	{
1926		AbsoluteTime 	expirationTime;
1927		TimerID			theTimer;
1928
1929		ih->timeout = false;
1930
1931		// expiration is current time + 1/15 second (2 frame times)
1932		expirationTime = AddDurationToAbsolute( kTimeoutDuration, UpTime());
1933
1934		result = SetInterruptTimer( &expirationTime, myTimerHandler, ih, &theTimer);
1935		FailWithVal( result != noErr, Exit, result);
1936
1937		while( deviceDescriptionPtr->complete == false && ih->timeout == false)	// loop until complete or timeout
1938			;
1939
1940		expirationTime = DurationToAbsolute( 0);
1941		CancelTimer( theTimer, &expirationTime);
1942
1943		pb->completionProc	= nil;
1944
1945		FailWithAction( ih->timeout == true, result = kIDHErrDeviceTimeout, Exit); //��� correct error code
1946	}
1947
1948Exit:
1949
1950	return result;
1951}
1952
1953#pragma mark -
1954#pragma mark ���������� IDHDV Private Component Calls  ����������
1955
1956pascal ComponentResult	IDHDVPrivateCall( ComponentInstance ihc, IDHDVPrivateSelector selector, void *privateData)
1957{
1958	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
1959	OSErr 						result = noErr;
1960
1961	switch (selector)
1962	{
1963		case kIDHDVPrivateSelectorDeviceAdded:
1964			RecordEventLogger( 'isoc', 'get ', 'priv', 'dadd');
1965			result = handleDeviceAdded(ihc, (GDFDeviceEventData*) privateData);
1966			break;
1967
1968		case kIDHDVPrivateSelectorDeviceRemoved:
1969			RecordEventLogger( 'isoc', 'get ', 'priv', 'remv');
1970			result = handleDeviceRemoved(ihc, (GDFDeviceEventData*) privateData);
1971			break;
1972	}
1973
1974	return result;
1975}
1976
1977#pragma mark -
1978#pragma mark ���������� IDH Internal Calls  ����������
1979
1980OSErr addBuffersToQueue( DeviceDescription *deviceDescriptionPtr)
1981{
1982	QElemPtr 		qelem;
1983	OSErr 			result = noErr;
1984	QueueElement	*qep;
1985	short			j;
1986
1987	if( countQueueElements( deviceDescriptionPtr->freeQueue) > 0 || countQueueElements( deviceDescriptionPtr->usedQueue) > 0)	// are there already buffers?
1988	{
1989		while( PBDequeueFirst( deviceDescriptionPtr->usedQueue, &qelem) == noErr)	// move buffers from used to free queue
1990		{
1991			((QueueElement *)qelem)->locks = 0;
1992
1993			PBEnqueueLast( qelem, deviceDescriptionPtr->freeQueue);
1994		}
1995	}
1996	else
1997	{
1998		for( j=0; j<kBuffersPerDevice; ++j)	// add buffers to this devices queues
1999		{
2000			qep = (QueueElement *) NewPtrSysClear( sizeof( QueueElement));
2001			FailWithAction( qep == nil, result = MemError(), Exit);
2002
2003			qep->buffer = NewPtrSys( (deviceDescriptionPtr->standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize);
2004			FailWithAction( qep->buffer == nil, result = MemError(), Exit);
2005
2006			qep->locks = 0;	// initialize to no client locks
2007
2008			PBEnqueueLast( (QElemPtr) qep, deviceDescriptionPtr->freeQueue);
2009		}
2010
2011		deviceDescriptionPtr->nQueueElements = kBuffersPerDevice;	// save number of buffers
2012	}
2013
2014Exit:
2015	return result;
2016
2017BadExit:
2018	// dispose buffers if error
2019	while( PBDequeueFirst( deviceDescriptionPtr->freeQueue, (QElemPtr *) &qep) == noErr)
2020	{
2021		if( qep)
2022		{
2023			if( qep->buffer)
2024				DisposePtr( qep->buffer);
2025
2026			DisposePtr( (char *) qep);
2027		}
2028	}
2029
2030	while( PBDequeueFirst( deviceDescriptionPtr->usedQueue, (QElemPtr *) &qep) == noErr)
2031	{
2032		if( qep)
2033		{
2034			if( qep->buffer)
2035				DisposePtr( qep->buffer);
2036
2037			DisposePtr( (char *) qep);
2038		}
2039	}
2040
2041	deviceDescriptionPtr->nQueueElements = 0;
2042
2043	return result;
2044}
2045
2046OSErr closeDeviceControl( ComponentInstance ihc, DeviceDescriptionPtr deviceDescriptionPtr)
2047{
2048	OSErr 						result = noErr;
2049	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
2050	IsochComponentGlobalsPtr	gGlobals = ih->gGlobals;
2051
2052	if( deviceDescriptionPtr->deviceControlInstance)
2053	{
2054		if( --deviceDescriptionPtr->deviceControlCount <= 0)
2055		{
2056			deviceDescriptionPtr->deviceControlCount = 0;
2057
2058			result = IDHCancelNotification( (ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr->deviceControlNotificationID);
2059			FailMessage( result != noErr);
2060
2061			result = IDHDisposeNotification((ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr->deviceControlNotificationID);
2062			FailMessage( result != noErr);
2063			deviceDescriptionPtr->deviceControlNotificationID = 0;
2064
2065			result = DeviceControlDisableAVCTransactions(deviceDescriptionPtr->deviceControlInstance);
2066			result = DeviceControlSetDeviceConnectionID(deviceDescriptionPtr->deviceControlInstance, 0);
2067
2068			CloseComponent(deviceDescriptionPtr->deviceControlInstance);
2069
2070			deviceDescriptionPtr->deviceControlInstance = nil;
2071		}
2072	}
2073
2074Exit:
2075	return result;
2076}
2077
2078OSErr checkSeed( IsochComponentGlobalsPtr gGlobals, QTAtomSpec *configID)
2079{
2080	QTAtom 		seedAtom;
2081	OSErr 		result = noErr;
2082	UInt32		seed;
2083
2084	// look for device in device container
2085	seedAtom = QTFindChildByIndex( configID->container, kParentAtomIsContainer, kIDHSeedAtomType, 1, nil);
2086	FailWithAction( seedAtom == nil, result = kIDHErrDeviceList, Exit);
2087
2088	QTLockContainer( configID->container);
2089
2090	// get the value of the devicePresent atom
2091	QTCopyAtomDataToPtr( configID->container, seedAtom, true, sizeof( seed), &seed, nil);
2092
2093	QTUnlockContainer( configID->container);
2094
2095	// seed has expired?
2096	if( seed != gGlobals->seed)
2097	{
2098		result = kIDHErrDeviceList;
2099		goto Exit;
2100	}
2101
2102Exit:
2103	return result;
2104}
2105
2106int countQueueElements( QHdrPtr queue)
2107{
2108	QElem *qptr;
2109	int nElements = 0;
2110
2111	qptr = queue->qHead;
2112	while( qptr)
2113	{
2114		++nElements;
2115		qptr = qptr->qLink;
2116	}
2117
2118	return nElements;
2119}
2120
2121// called when a device is added/removed and updates device control clientID
2122OSStatus deviceControlCallback( IDHGenericEvent *event, void *userData)
2123{
2124	OSErr 						result = noErr;
2125	DeviceDescription			*deviceDescriptionPtr = (DeviceDescriptionPtr) userData;
2126	IsochComponentGlobalsPtr	gGlobals = deviceDescriptionPtr->componentGlobals;
2127
2128	RecordEventLogger( 'isoc', 'devc', 'calb', event->eventHeader.event);
2129
2130	switch( event->eventHeader.event)
2131	{
2132		case kIDHEventDeviceAdded:
2133
2134			result = DeviceControlDisableAVCTransactions(deviceDescriptionPtr->deviceControlInstance);
2135			FailMessageVal( result != noErr, result);
2136
2137			result = DeviceControlSetDeviceConnectionID(deviceDescriptionPtr->deviceControlInstance,
2138						(DeviceConnectionID) deviceDescriptionPtr->fwClientID);
2139			FailWithVal( result != noErr, Exit, result);
2140
2141			result = DeviceControlEnableAVCTransactions(deviceDescriptionPtr->deviceControlInstance);
2142			FailWithVal( result != noErr, Exit, result);
2143
2144			break;
2145
2146		case kIDHEventDeviceRemoved:
2147			result = DeviceControlDisableAVCTransactions(deviceDescriptionPtr->deviceControlInstance);
2148			FailWithVal( result != noErr, Exit, result);
2149
2150			break;
2151
2152		default:
2153			FailMessage( (event->eventHeader.event & (kIDHEventDeviceRemoved | kIDHEventDeviceAdded)) == nil);
2154			break;
2155	}
2156
2157	result = IDHNotifyMeWhen( (ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr->deviceControlNotificationID,
2158				kIDHEventDeviceAdded | kIDHEventDeviceRemoved);
2159	FailWithVal( result != noErr, Exit, result);
2160
2161Exit:
2162	return result;
2163}
2164
2165OSErr doAVCTransaction( FWReferenceID refID, DVCTransactionParams* inTransaction)
2166{
2167	FWCommandObjectID 	commandObjectID;
2168	OSErr 				result = noErr;
2169	FCPResponseHandler	*responseHandler = 0;
2170	UInt32 				fwCommandFlags = kFWCommandSyncFlag;
2171
2172	// allocate command object for sending FCP commands synchronously.
2173	result = FWAllocateFCPCommandObject(&commandObjectID);
2174	FailWithVal( result != noErr, Exit1, result);
2175
2176	// Set up FCP command params to tell camera to do something.
2177	result = FWSetFWCommandParams(	commandObjectID,							// objectID
2178									refID,										// ref ID
2179									fwCommandFlags,								// cmd flags
2180									nil,										// completion proc
2181									0);											// completion data
2182	FailWithVal( result != noErr, Exit, result);
2183
2184
2185	result = FWSetFCPCommandParams(	commandObjectID,							// objectID
2186									inTransaction->commandBufferPtr,			// cmd buffer
2187									inTransaction->commandLength,				// cmd length
2188									inTransaction->responseBufferPtr,			// response buffer
2189									inTransaction->responseBufferSize,			// response size
2190									100 * durationMillisecond,					// timeout
2191									8,											// max retries
2192									0,											// transfer flags
2193									responseHandler );							// response handler
2194	FailWithVal( result != noErr, Exit, result);
2195
2196	// send the FCP command
2197	result = FWSendFCPCommand(commandObjectID);
2198	FailWithVal( result != noErr, Exit, result);
2199
2200Exit:
2201	FWDeallocateFWCommandObject(commandObjectID);
2202
2203Exit1:
2204	return result;
2205}
2206
2207OSErr findDeviceDescriptionforDevice( IsochComponentInstancePtr ih, UInt32 deviceID, DeviceDescription **deviceDescription)
2208{
2209	OSErr 						result = noErr;
2210	IsochComponentGlobalsPtr 	gGlobals = ih->gGlobals;
2211	short						i;
2212
2213	*deviceDescription = nil;
2214	for( i=0; i<kMaxDevicesActive; ++i)
2215	{
2216		if( gGlobals->deviceDescription[i].deviceID == deviceID)
2217		{
2218			*deviceDescription = &gGlobals->deviceDescription[i];
2219			break;
2220		}
2221	}
2222
2223	if( *deviceDescription == nil)
2224		result = kIDHErrInvalidDeviceID;
2225
2226Exit:
2227	return result;
2228}
2229
2230//��� do a super find atom someday
2231OSErr findAtom( QTAtomSpec *atomSpec, OSType theType, QTAtom *theAtom)
2232{
2233	OSErr result = noErr;
2234	OSType type;
2235	QTAtom atom;
2236
2237	atom = atomSpec->atom;
2238
2239	result = QTGetAtomTypeAndID( atomSpec->container, atom, (long *) &type, nil);
2240	FailWithVal( result != noErr, Exit, result);
2241
2242	while( type != kIDHDeviceAtomType && type != theType)
2243	{
2244		atom = QTGetAtomParent( atomSpec->container, atom); // should be isoch atom
2245		FailWithAction( atom == nil || atom == -1, result = kIDHErrDeviceList, Exit);
2246
2247		result = QTGetAtomTypeAndID( atomSpec->container, atom, (long *) &type, nil);
2248		FailWithVal( result != noErr, Exit, result);
2249	}
2250
2251	if( theType == type)
2252	{
2253		*theAtom = atom;
2254		result = noErr;
2255	}
2256	else
2257	{
2258		*theAtom = nil;
2259		result = kIDHErrDeviceList;
2260	}
2261
2262Exit:
2263	return result;
2264}
2265
2266OSErr getDeviceID( QTAtomSpec *configID, UInt32 *deviceID)
2267{
2268	OSErr result = noErr;
2269	QTAtom deviceAtom;
2270
2271	*deviceID = nil;
2272
2273	result = findAtom( configID, kIDHDeviceAtomType, &deviceAtom);
2274	FailWithVal( result != noErr, Exit, result);
2275
2276	result = QTGetAtomTypeAndID( configID->container, deviceAtom, nil, (long *) deviceID);
2277	FailWithVal( result != noErr, Exit, result);
2278
2279Exit:
2280	return result;
2281}
2282
2283
2284OSErr getDeviceStandard( FWReferenceID clientID, UInt32 * pStandard )
2285{
2286	AVCCTSFrameStruct		avcFrame;
2287	DVCTransactionParams	transactionParams;
2288	UInt8					responseBuffer[ 16 ];
2289	OSErr					theErr = noErr;
2290	UInt32					currentSignal, AVCStatus;
2291
2292	*pStandard = ntscIn;
2293
2294	// fill up the avc frame
2295	avcFrame.cmdType_respCode	= kAVCStatusInquiryCommand;
2296	avcFrame.headerAddress 		= 0x20;						// for now
2297	avcFrame.opcode 			= kAVCOutputSignalModeOpcode;
2298	avcFrame.operand[ 0 ] 		= kAVCSignalModeDummyOperand;
2299
2300	// fill up the transaction parameter block
2301	transactionParams.commandBufferPtr		= (Ptr) &avcFrame;
2302	transactionParams.commandLength			= 4;
2303	transactionParams.responseBufferPtr		= (Ptr) responseBuffer;
2304	transactionParams.responseBufferSize	= 16;
2305	transactionParams.responseHandler		= nil;
2306
2307//	some cameras need some time before they can respond to this command
2308	DelayFor ( 1 * durationSecond );
2309
2310	theErr = setupAVCTransaction( clientID, &transactionParams );
2311	FailWithVal( theErr != noErr, Exit, theErr);
2312
2313	currentSignal = ((responseBuffer[ 2 ] << 8) | responseBuffer[ 3 ]);
2314	AVCStatus = responseBuffer[ 0 ];
2315
2316	switch (currentSignal & 0x000000ff)
2317	{
2318		case kAVCSignalModeSD525_60:
2319		case kAVCSignalModeSDL525_60:
2320		case kAVCSignalModeHD1125_60:
2321			*pStandard = ntscIn;
2322			break;
2323
2324		case kAVCSignalModeSD625_50:
2325		case kAVCSignalModeSDL625_50:
2326		case kAVCSignalModeHD1250_50:
2327			*pStandard = palIn;
2328			break;
2329	}
2330
2331Exit:
2332
2333// JKL, because of devices that do not have device control, it is probably not safe
2334// to return an error here. Just default to NTSC. Need a better solution for this:
2335// user input? examining the stream? If this is just for setting the buffer size
2336// might be better to waste 100k in the NTSC case and just use PAL buffer size.
2337	if (theErr != noErr)
2338	{
2339		*pStandard = ntscIn;
2340		theErr = noErr;
2341	}
2342
2343	return theErr;
2344}
2345
2346OSStatus localCompletionRoutine( IDHGenericEvent *event, void *userData)
2347{
2348#pragma unused (event)
2349
2350	DeviceDescription *deviceDescriptionPtr = (DeviceDescription *) userData;
2351
2352	deviceDescriptionPtr->complete = true;	// tell the read/write operation that operation is complete
2353
2354	return noErr;
2355}
2356
2357OSStatus myTimerHandler( void *p1, void *p2)
2358{
2359#pragma unused( p2)
2360
2361	IsochComponentInstancePtr ih = (IsochComponentInstancePtr) p1;
2362
2363	ih->timeout = true;
2364
2365	return noErr;
2366}
2367
2368OSErr postEvent(IsochComponentGlobals* gGlobals,
2369		IDHDeviceID deviceID,
2370		IDHEvent event,
2371		void* privateData)
2372{
2373	UInt32 i;
2374	IDHDeviceConnectionEvent connectionEvent;
2375	OSErr error = noErr;
2376
2377	// We now have two broad classifications of events - ones that need to be
2378	// reported ASAP, which are stream related:
2379	//
2380	// 		kIDHPrivateEventReadComplete
2381	//		kIDHPrivateEventWriteComplete
2382	//
2383	// and ones that are device management related, whose notifications will
2384	// probably generate massive amounts of task-level only Toolbox calls:
2385	//
2386	//		kIDHEventDeviceAdded
2387	//		kIDHEventDeviceRemoved
2388	//		kIDHEventReadEnabled
2389	//		kIDHEventReadDisabled
2390	//		kIDHEventWriteEnabled
2391	//		kIDHEventWriteDisabled
2392	//
2393	// kIDHPrivateEventReadComplete and kIDHPrivateEventWriteComplete are posted to a secondary
2394	// interrupt handler. All other events are handled immediately.
2395
2396
2397	RecordEventLogger( 'isoc', 'post', deviceID, event);
2398
2399	for (i = 0; i < kMaxNotifications ; ++i)
2400	{
2401		ClientNotification* clientNotification = &gGlobals->clientNotification[i];
2402		IDHDeviceID clientDeviceID = clientNotification->deviceID;
2403		IDHEvent wantedEvents = clientNotification->events;
2404
2405		if (event & wantedEvents)
2406		{
2407			// Send notification if the device matches the client's desired device
2408			// or client specified all devices.
2409			if ((kIDHDeviceIDEveryDevice == clientDeviceID) ||
2410				(deviceID == clientDeviceID) )
2411			{
2412				// we currently only support a one-shot notification, like clock callbacks
2413				clientNotification->events = 0;
2414
2415				switch(event)
2416				{
2417					case kIDHPrivateEventReadComplete:
2418					case kIDHPrivateEventWriteComplete:
2419
2420						(*clientNotification->notificationProc)
2421								((IDHGenericEvent*)privateData,
2422								clientNotification->userData);
2423						break;
2424
2425					case kIDHEventDeviceChanged:
2426					case kIDHEventDeviceAdded:
2427					case kIDHEventDeviceRemoved:
2428					case kIDHEventReadEnabled:
2429					case kIDHEventReadDisabled:
2430					case kIDHEventWriteEnabled:
2431					case kIDHEventWriteDisabled:
2432
2433						connectionEvent.eventHeader.event = event;
2434						connectionEvent.eventHeader.deviceID = deviceID;
2435						connectionEvent.eventHeader.notificationID =
2436								(UInt32)clientNotification;
2437
2438						(*clientNotification->notificationProc)
2439								((IDHGenericEvent*)&connectionEvent,
2440								clientNotification->userData);
2441						break;
2442
2443					default:
2444						RecordEventLogger( 'isoc', 'post', '????', event);
2445						break;
2446				}
2447
2448			}
2449		}
2450	}
2451
2452	return error;
2453
2454}
2455
2456
2457OSStatus postEventSIH(void* p1, void* p2)
2458{
2459	ClientNotification			*clientNotification = (ClientNotification *) p1;
2460 	IDHGenericEvent				*privateData =  (IDHGenericEvent *) p2;
2461	OSStatus					status = noErr;
2462
2463	// call the routine
2464	(*clientNotification->notificationProc) (privateData, clientNotification->userData);
2465
2466	return status;
2467}
2468
2469
2470OSStatus readNotifyProc( IDHGenericEvent *event, void *userData)
2471{
2472	OSErr 						result = noErr;
2473	IsochComponentGlobalsPtr	gGlobals = userData;
2474	IDHDVCompleteEvent*	 		readCompletePtr = (IDHDVCompleteEvent*) event;
2475	DeviceDescription			*deviceDescriptionPtr = nil;
2476	short						i;
2477	QueueElementPtr				qep;
2478
2479	RecordEventLogger( 'isoc', 'read', 'comp', ((char *) readCompletePtr->frameBuffer)[640] & 0xf);
2480
2481	FailIf( event->eventHeader.event != kIDHPrivateEventReadComplete, Exit);
2482
2483	// find the device description that belongs to this read callback
2484	deviceDescriptionPtr = nil;
2485	for( i=0; i<kMaxDevicesActive; ++i)
2486	{
2487		if( gGlobals->deviceDescription[i].deviceID == event->eventHeader.deviceID)
2488		{
2489			deviceDescriptionPtr = &gGlobals->deviceDescription[i];
2490			break;
2491		}
2492	}
2493
2494	FailIf( deviceDescriptionPtr == nil, Exit);	// couldn't find device in tree
2495
2496	// find a buffer to copy the data into
2497	if( (result = PBDequeueFirst( deviceDescriptionPtr->freeQueue, (QElemPtr *) &qep)) != noErr)
2498		if( (result = PBDequeueFirst( deviceDescriptionPtr->usedQueue, (QElemPtr *) &qep)) != noErr) //��� changes when lock
2499		{
2500			FailWithVal( result != noErr, Exit, result); // no queue elems available
2501		}
2502
2503	qep->locks = 0;	// reset lock
2504
2505	// make sure we have enuf room
2506	FailIf( readCompletePtr->bufferSize > kPALCompressedBufferSize, Exit);
2507//	(deviceDescriptionPtr->standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize, Exit);
2508
2509	// copy the data and save the size
2510	BlockMoveData( readCompletePtr->frameBuffer, qep->buffer, readCompletePtr->bufferSize);
2511	qep->size = readCompletePtr->bufferSize;
2512
2513	// find all of instances that need to be serviced for this device
2514	for( i=0; i<kMaxInstances; ++i)
2515	{
2516		IsochComponentInstancePtr	ih = deviceDescriptionPtr->instancePtr[i];
2517		IDHParameterBlock 			*readRequest;
2518
2519		if( deviceDescriptionPtr->instancePtr[i] == nil)	// no instance?
2520			continue;
2521
2522		// get a read request
2523		if( PBDequeueFirst( ih->readQueue, (QElemPtr *) &readRequest) != noErr)
2524			continue;
2525
2526		readRequest->actualCount = 0;
2527		readRequest->result = noErr;
2528
2529		// does the user want an internal buffer or copy to user buffer
2530		if( readRequest->buffer == nil)
2531		{
2532			RecordEventLogger( 'isoc', 'redc', 'read', 'nil ');
2533
2534			readRequest->buffer 		= readCompletePtr->frameBuffer;	// return internal buffer
2535			readRequest->actualCount 	= readCompletePtr->bufferSize;
2536
2537			++qep->locks;	// add a lock to this buffer
2538		}
2539		else
2540		{
2541			RecordEventLogger( 'isoc', 'redc', 'read', 'buff');
2542
2543			if( readCompletePtr->bufferSize <= readRequest->requestedCount) // do we have enuf room?
2544			{
2545				// copy the buffer into client buffer
2546				BlockMoveData( readCompletePtr->frameBuffer, readRequest->buffer, readCompletePtr->bufferSize);
2547				readRequest->actualCount = readCompletePtr->bufferSize;
2548			}
2549		}
2550
2551		// call the client callback
2552		result = (*readRequest->completionProc)( (IDHGenericEvent *) readRequest, readRequest->refCon);
2553
2554	} // end of processing instances
2555
2556	if( qep->locks > 0)	// if this buffer has locks, move it to the usedQueue else requeue on free queue
2557		PBEnqueueLast( (QElemPtr) qep, deviceDescriptionPtr->usedQueue);
2558	else
2559		PBEnqueueLast( (QElemPtr) qep, deviceDescriptionPtr->freeQueue);
2560
2561	// reset read complete notification
2562	result = IDHNotifyMeWhen( (ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr->readIsochNotificationID,
2563				kIDHPrivateEventReadComplete);
2564
2565Exit:
2566	return result;
2567}
2568
2569OSErr setupAVCTransaction( FWReferenceID refID, DVCTransactionParams *pParams )
2570{
2571	DVCTransactionParams		transactionParams;
2572	OSErr						error = noErr;
2573
2574	// fill out the internal transaction block
2575	transactionParams.commandBufferPtr		= pParams->commandBufferPtr;
2576	transactionParams.commandLength			= pParams->commandLength;
2577	transactionParams.responseBufferPtr		= pParams->responseBufferPtr;
2578	transactionParams.responseBufferSize	= pParams->responseBufferSize;
2579	transactionParams.responseHandler		= nil; //��� pParams->responseHandler;
2580
2581	error = doAVCTransaction( refID, &transactionParams );
2582	FailWithVal( error != noErr, Exit, error);
2583
2584Exit:
2585	return( error );
2586}
2587
2588OSErr setupVideoAtoms( QTAtomContainer container, QTAtom isocAtom, UInt32 standard) // add DV NTSC config
2589{
2590	OSErr 					result = noErr;
2591	QTAtom 					configAtom;
2592	OSType 					type;
2593	long 					size;
2594	float 					interval;
2595	long					direction;
2596	IDHDimension 			dimension;
2597	IDHResolution 			resolution;
2598	Fixed 					refresh;
2599	OSType 					pixel;
2600	OSType 					decoType;
2601	Component 				decoComponent;
2602	ComponentDescription 	compDescrip;
2603
2604	// create a vide NTSC mode
2605	result = QTInsertChild( container, isocAtom, kIDHIsochModeAtomType,
2606			0, 0, 0, nil, &configAtom);
2607	FailWithVal( result != noErr, Exit, result);
2608
2609	type = kIDHVideoMediaAtomType;
2610	result = QTInsertChild( container, configAtom, kIDHIsochMediaType,
2611			0, 0, sizeof( type), &type, nil);
2612	FailWithVal( result != noErr, Exit, result);
2613
2614	// for compatibility with Hemingway releases prior to our d13 release
2615	result = QTInsertChild( container, configAtom, kIDHNameAtomType,
2616			0, 0, 3, "\pDV", nil);
2617	FailWithVal( result != noErr, Exit, result);
2618
2619	type = (standard == ntscIn)?'DVC ':'DVCP';
2620	result = QTInsertChild( container, configAtom, kIDHDataTypeAtomType,
2621			0, 0, sizeof( type), &type, nil);
2622	FailWithVal( result != noErr, Exit, result);
2623
2624	size = (standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize;
2625	result = QTInsertChild( container, configAtom, kIDHDataSizeAtomType,
2626			0, 0, sizeof( size), &size, nil);
2627	FailWithVal( result != noErr, Exit, result);
2628
2629	size = (standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize;
2630	result = QTInsertChild( container, configAtom, kIDHDataBufferSizeAtomType,
2631			0, 0, sizeof( size), &size, nil);
2632	FailWithVal( result != noErr, Exit, result);
2633
2634	interval = 29.97;
2635	result = QTInsertChild( container, configAtom, kIDHDataIntervalAtomType,
2636			0, 0, sizeof( interval), &interval, nil);
2637	FailWithVal( result != noErr, Exit, result);
2638
2639	direction = kIDHDataTypeIsInputAndOutput;
2640	result = QTInsertChild( container, configAtom, kIDHDataIODirectionAtomType,
2641			0, 0, sizeof( direction), &direction, nil);
2642	FailWithVal( result != noErr, Exit, result);
2643
2644	dimension.x = 720; dimension.y = (standard == ntscIn)?480:576;
2645	result = QTInsertChild( container, configAtom, kIDHVideoDimensionsAtomType,
2646			0, 0, sizeof( dimension), &dimension, nil);
2647	FailWithVal( result != noErr, Exit, result);
2648
2649	resolution.x = 72 << 16; resolution.y = 72 << 16;
2650	result = QTInsertChild( container, configAtom, kIDHVideoResolutionAtomType,
2651			0, 0, sizeof( resolution), &resolution, nil);
2652	FailWithVal( result != noErr, Exit, result);
2653
2654	refresh = (29 << 16) + 97; //���
2655	result = QTInsertChild( container, configAtom, kIDHVideoRefreshRateAtomType,
2656			0, 0, sizeof( refresh), &refresh, nil);
2657	FailWithVal( result != noErr, Exit, result);
2658
2659	pixel = 'dv  '; //���
2660	result = QTInsertChild( container, configAtom, kIDHVideoPixelTypeAtomType,
2661			0, 0, sizeof( pixel), &pixel, nil);
2662	FailWithVal( result != noErr, Exit, result);
2663
2664//���	kIDHVideoDecompressorsAtomType		= FOUR_CHAR_CODE('deco'),
2665
2666	decoType = (standard == ntscIn)?'dvc ':'dvcp';
2667	result = QTInsertChild( container, configAtom, kIDHVideoDecompressorTypeAtomType,
2668			0, 0, sizeof( decoType), &decoType, nil);
2669	FailWithVal( result != noErr, Exit, result);
2670
2671	compDescrip.componentType = 'imdc';
2672	compDescrip.componentSubType = decoType;
2673	compDescrip.componentManufacturer = 0;
2674	compDescrip.componentFlags = 0;
2675	compDescrip.componentFlagsMask = 0;
2676
2677	decoComponent = FindNextComponent( nil, &compDescrip);
2678	result = QTInsertChild( container, configAtom, kIDHVideoDecompressorComponentAtomType,
2679			0, 0, sizeof( decoComponent), &decoComponent, nil);
2680	FailWithVal( result != noErr, Exit, result);
2681
2682Exit:
2683	return result;
2684}
2685
2686OSErr setup48kAudioAtoms( QTAtomContainer container, QTAtom isocAtom, UInt32 standard)
2687{
2688	OSErr 		err;
2689	QTAtom 		configAtom;
2690
2691	StringPtr 	name;
2692	OSType 		type;
2693	long 		size;
2694	Fixed 		rate;
2695	float 		interval;
2696	long 		direction;
2697
2698	// create a vide NTSC mode
2699	err = QTInsertChild( container, isocAtom, kIDHIsochModeAtomType,
2700			0, 0, 0, nil, &configAtom);
2701	FailWithVal( err != noErr, Exit, err);
2702
2703	type = kIDHSoundMediaAtomType;
2704	err = QTInsertChild( container, configAtom, kIDHIsochMediaType,
2705			0, 0, sizeof( type), &type, nil);
2706	FailWithVal( err != noErr, Exit, err);
2707
2708	name = "\pDV-48khz";
2709	err = QTInsertChild( container, configAtom, kIDHNameAtomType,
2710			0, 0, name[0], name, nil);
2711	FailWithVal( err != noErr, Exit, err);
2712
2713	type = 'DV48';
2714	err = QTInsertChild( container, configAtom, kIDHDataTypeAtomType,
2715			0, 0, sizeof( type), &type, nil);
2716	FailWithVal( err != noErr, Exit, err);
2717
2718	size = (standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize;
2719	err = QTInsertChild( container, configAtom, kIDHDataSizeAtomType,
2720			0, 0, sizeof( size), &size, nil);
2721	FailWithVal( err != noErr, Exit, err);
2722
2723	size = (standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize;
2724	err = QTInsertChild( container, configAtom, kIDHDataBufferSizeAtomType,
2725			0, 0, sizeof( size), &size, nil);
2726	FailWithVal( err != noErr, Exit, err);
2727
2728	interval = 29.97;
2729	err = QTInsertChild( container, configAtom, kIDHDataIntervalAtomType,
2730			0, 0, sizeof( interval), &interval, nil);
2731	FailWithVal( err != noErr, Exit, err);
2732
2733	direction = kIDHDataTypeIsInputAndOutput;
2734	err = QTInsertChild( container, configAtom, kIDHDataIODirectionAtomType,
2735			0, 0, sizeof( direction), &direction, nil);
2736	FailWithVal( err != noErr, Exit, err);
2737
2738	size = 2;
2739	err = QTInsertChild( container, configAtom, kIDHSoundChannelCountAtomType,
2740			0, 0, sizeof( size), &size, nil);
2741	FailWithVal( err != noErr, Exit, err);
2742
2743	size = 2;
2744	err = QTInsertChild( container, configAtom, kIDHSoundSampleSizeAtomType,
2745			0, 0, sizeof( size), &size, nil);
2746	FailWithVal( err != noErr, Exit, err);
2747
2748	rate = rate44khz;
2749	err = QTInsertChild( container, configAtom, kIDHSoundSampleRateAtomType,
2750			0, 0, sizeof( rate), &rate, nil);
2751	FailWithVal( err != noErr, Exit, err);
2752
2753Exit:
2754	return err;
2755} // sound 1 config
2756
2757OSErr setup32kAudioAtoms( QTAtomContainer container, QTAtom isocAtom, UInt32 standard)
2758{
2759	OSErr err;
2760	QTAtom configAtom;
2761
2762	StringPtr 	name;
2763	OSType 		type;
2764	long 		size;
2765	Fixed 		rate;
2766	float 		interval;
2767	long 		direction;
2768
2769	// create a vide NTSC mode
2770	err = QTInsertChild( container, isocAtom, kIDHIsochModeAtomType,
2771			0, 0, 0, nil, &configAtom);
2772	FailWithVal( err != noErr, Exit, err);
2773
2774	type = kIDHSoundMediaAtomType;
2775	err = QTInsertChild( container, configAtom, kIDHIsochMediaType,
2776			0, 0, sizeof( type), &type, nil);
2777	FailWithVal( err != noErr, Exit, err);
2778
2779	name = "\pDV-32khz";
2780	err = QTInsertChild( container, configAtom, kIDHNameAtomType,
2781			0, 0, name[0], name, nil);
2782	FailWithVal( err != noErr, Exit, err);
2783
2784	type = 'DV32';
2785	err = QTInsertChild( container, configAtom, kIDHDataTypeAtomType,
2786			0, 0, sizeof( type), &type, nil);
2787	FailWithVal( err != noErr, Exit, err);
2788
2789	size = (standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize;
2790	err = QTInsertChild( container, configAtom, kIDHDataSizeAtomType,
2791			0, 0, sizeof( size), &size, nil);
2792	FailWithVal( err != noErr, Exit, err);
2793
2794	size = (standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize;
2795	err = QTInsertChild( container, configAtom, kIDHDataBufferSizeAtomType,
2796			0, 0, sizeof( size), &size, nil);
2797	FailWithVal( err != noErr, Exit, err);
2798
2799	interval = 29.97;
2800	err = QTInsertChild( container, configAtom, kIDHDataIntervalAtomType,
2801			0, 0, sizeof( interval), &interval, nil);
2802	FailWithVal( err != noErr, Exit, err);
2803
2804	direction = kIDHDataTypeIsInputAndOutput;
2805	err = QTInsertChild( container, configAtom, kIDHDataIODirectionAtomType,
2806			0, 0, sizeof( direction), &direction, nil);
2807	FailWithVal( err != noErr, Exit, err);
2808
2809	size = 4;
2810	err = QTInsertChild( container, configAtom, kIDHSoundChannelCountAtomType,
2811			0, 0, sizeof( size), &size, nil);
2812	FailWithVal( err != noErr, Exit, err);
2813
2814	size = 2;
2815	err = QTInsertChild( container, configAtom, kIDHSoundSampleSizeAtomType,
2816			0, 0, sizeof( size), &size, nil);
2817	FailWithVal( err != noErr, Exit, err);
2818
2819	rate = 32000 << 16;
2820	err = QTInsertChild( container, configAtom, kIDHSoundSampleRateAtomType,
2821			0, 0, sizeof( rate), &rate, nil);
2822	FailWithVal( err != noErr, Exit, err);
2823
2824Exit:
2825	return err;
2826} // sound 2 config
2827
2828OSErr setup44kAudioAtoms( QTAtomContainer container, QTAtom isocAtom, UInt32 standard)
2829{
2830	OSErr err;
2831	QTAtom configAtom;
2832
2833	StringPtr 	name;
2834	OSType 		type;
2835	long 		size;
2836	Fixed 		rate;
2837	float 		interval;
2838	long 		direction;
2839
2840	// create a vide NTSC mode
2841	err = QTInsertChild( container, isocAtom, kIDHIsochModeAtomType,
2842			0, 0, 0, nil, &configAtom);
2843	FailWithVal( err != noErr, Exit, err);
2844
2845	type = kIDHSoundMediaAtomType;
2846	err = QTInsertChild( container, configAtom, kIDHIsochMediaType,
2847			0, 0, sizeof( type), &type, nil);
2848	FailWithVal( err != noErr, Exit, err);
2849
2850	name = "\pDV-44khz";
2851	err = QTInsertChild( container, configAtom, kIDHNameAtomType,
2852			0, 0, name[0], name, nil);
2853	FailWithVal( err != noErr, Exit, err);
2854
2855	type = 'DV44';
2856	err = QTInsertChild( container, configAtom, kIDHDataTypeAtomType,
2857			0, 0, sizeof( type), &type, nil);
2858	FailWithVal( err != noErr, Exit, err);
2859
2860	size = (standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize;
2861	err = QTInsertChild( container, configAtom, kIDHDataSizeAtomType,
2862			0, 0, sizeof( size), &size, nil);
2863	FailWithVal( err != noErr, Exit, err);
2864
2865	size = (standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize;
2866	err = QTInsertChild( container, configAtom, kIDHDataBufferSizeAtomType,
2867			0, 0, sizeof( size), &size, nil);
2868	FailWithVal( err != noErr, Exit, err);
2869
2870	interval = 29.97;
2871	err = QTInsertChild( container, configAtom, kIDHDataIntervalAtomType,
2872			0, 0, sizeof( interval), &interval, nil);
2873	FailWithVal( err != noErr, Exit, err);
2874
2875	direction = kIDHDataTypeIsInputAndOutput;
2876	err = QTInsertChild( container, configAtom, kIDHDataIODirectionAtomType,
2877			0, 0, sizeof( direction), &direction, nil);
2878	FailWithVal( err != noErr, Exit, err);
2879
2880	size = 4;
2881	err = QTInsertChild( container, configAtom, kIDHSoundChannelCountAtomType,
2882			0, 0, sizeof( size), &size, nil);
2883	FailWithVal( err != noErr, Exit, err);
2884
2885	size = 2;
2886	err = QTInsertChild( container, configAtom, kIDHSoundSampleSizeAtomType,
2887			0, 0, sizeof( size), &size, nil);
2888	FailWithVal( err != noErr, Exit, err);
2889
2890	rate = 44100 << 16;
2891	err = QTInsertChild( container, configAtom, kIDHSoundSampleRateAtomType,
2892			0, 0, sizeof( rate), &rate, nil);
2893	FailWithVal( err != noErr, Exit, err);
2894
2895Exit:
2896	return err;
2897} // sound 3 config
2898
2899OSErr updateDeviceList(IsochComponentInstancePtr ih, GDFDeviceEventData *pGDFData, IDHDeviceID *deviceID, Boolean doAdd)
2900{
2901	OSErr 						result = noErr;
2902	QTAtom 						deviceAtom, isocAtom;
2903	IsochComponentGlobalsPtr 	gGlobals = ih->gGlobals;
2904	Boolean						listChanged = false, foundDevice = false;
2905	DeviceDescription			*deviceDescriptionPtr;
2906	IDHDeviceStatus				deviceStatus;
2907	UInt32						standard;
2908	UInt32						devIndex;
2909	FWReferenceID				localFWReferenceID;
2910	ComponentDescription		clkDesc;
2911
2912	RecordEventLogger( 'isoc', 'updt', pGDFData->driverRefNum, 0);
2913
2914	++gGlobals->seed;
2915
2916	if (doAdd)
2917	{
2918		// The refNum of a driver is NOT a constant IF the driver is allowed to close
2919		// once it has been opened. Our expert never allows the driver to be closed.
2920		// So use that refNum to search for the device in.
2921		// The same provision holds true for the RegEntryID so we could do that.
2922
2923		// look for existing device
2924		for( devIndex=0; devIndex < gGlobals->nDevices; ++devIndex)
2925		{
2926			if (pGDFData->driverRefNum == gGlobals->deviceDescription[devIndex].refNum)
2927			{
2928				FWDriverID clientID = gGlobals->deviceDescription[devIndex].fwClientID;
2929				gGlobals->deviceDescription[devIndex].active = true;
2930				foundDevice = true;
2931
2932				// max packet size for s100, set again in case clientID changes
2933				FWSetMaxPayloadSize(clientID, 512);
2934
2935				// Driver/RegEntry ID would change if device wasplugged into a different
2936				// interface/card. Can fix in future by looking at UniqueID in "new"
2937				// devices and see if they match any of the old devices.
2938
2939				// if there are more than one FW interfaces on the machine, its conceivable
2940				// that the same device could get plugged into a different interface
2941				// update the local FW reference just in case
2942				result = FWGetLocalFWReferenceIDFromFWReferenceID(clientID, &localFWReferenceID);
2943				FailWithVal( result != noErr, Exit, result);
2944				gGlobals->deviceDescription[devIndex].localFWReferenceID = localFWReferenceID;
2945
2946				*deviceID = gGlobals->deviceDescription[devIndex].deviceID;
2947			}
2948		}
2949
2950		if (!foundDevice)
2951		{
2952			FWDriverID clientID;
2953			CSRNodeUniqueID csrNodeUniqueID;
2954			Str255 cameraName;
2955
2956			deviceDescriptionPtr = &gGlobals->deviceDescription[gGlobals->nDevices];
2957
2958			result = registerDevice(
2959					&pGDFData->deviceRegEntryID,
2960					&clientID,
2961					deviceDescriptionPtr);
2962
2963			FailIf(noErr != result, Exit);
2964
2965			RecordEventLogger( 'isoc', 'updt', 'add ', 'reg ');
2966
2967			result = FWGetUniqueID(clientID, &csrNodeUniqueID);
2968			FailIf( result != noErr, Exit);
2969
2970			// add its description
2971			gGlobals->nDevices++;
2972
2973			result = FWGetLocalFWReferenceIDFromFWReferenceID(clientID, &localFWReferenceID);
2974			FailWithVal( result != noErr, Exit, result);
2975
2976			*deviceID = gGlobals->nDevices;
2977
2978			deviceDescriptionPtr->componentGlobals		= gGlobals;
2979			deviceDescriptionPtr->regEntryID 			= pGDFData->deviceRegEntryID;
2980			deviceDescriptionPtr->refNum 				= pGDFData->driverRefNum;
2981			deviceDescriptionPtr->deviceID 				= *deviceID;
2982			deviceDescriptionPtr->fwClientID 			= clientID;
2983			deviceDescriptionPtr->localFWReferenceID 	= localFWReferenceID;
2984			deviceDescriptionPtr->uniqueID.hi 			= csrNodeUniqueID.hi;
2985			deviceDescriptionPtr->uniqueID.lo 			= csrNodeUniqueID.lo;
2986			deviceDescriptionPtr->deviceControlNotificationID = 0;
2987
2988			// max packet size for s100
2989			FWSetMaxPayloadSize(clientID, 512);
2990
2991			// add the device standard (PAL, NTSC)
2992			result = getDeviceStandard(clientID, &standard);
2993			FailWithVal( result != noErr, Exit, result);
2994
2995			deviceDescriptionPtr->standard 				= standard;
2996			deviceDescriptionPtr->isochPortID 			= 0;
2997			deviceDescriptionPtr->outputIsochChannelID 	= 0;
2998			deviceDescriptionPtr->inputIsochChannelID 	= 0;
2999			deviceDescriptionPtr->pGlobalDVOutData 		= nil;
3000			deviceDescriptionPtr->pGlobalDVInData 		= nil;
3001			deviceDescriptionPtr->dclProgramID 			= 0;
3002			deviceDescriptionPtr->active 				= true;
3003
3004			// allocate command objects that may be used at non-task level
3005			// jkl, do these need to be deallocated somewhere?
3006			result = FWAllocateIsochPortCommandObject(&deviceDescriptionPtr->isochPortCommandObjectID);
3007			FailWithVal( result != noErr, Exit, result);
3008			result = FWAllocateIsochPortCommandObject(&deviceDescriptionPtr->stopIsochPortCommandObjectID);
3009			FailWithVal( result != noErr, Exit, result);
3010			result = FWAllocateIsochPortCommandObject(&deviceDescriptionPtr->startIsochPortCommandObjectID);
3011			FailWithVal( result != noErr, Exit, result);
3012			result = FWAllocateAsynchCommandObject(&deviceDescriptionPtr->asynchCommandObjectID);
3013			FailWithVal( result != noErr, Exit, result);
3014			FWSetAsynchCommandMaxPayloadSize(deviceDescriptionPtr->asynchCommandObjectID, 512);
3015
3016			// find clock component
3017			// wouldn't it be better for us to open an instance on OpenDevice, set FWClockPrivLocalReference, etc.
3018			clkDesc.componentType 			= clockComponentType;
3019			clkDesc.componentSubType 		= 'fwcy';
3020			clkDesc.componentManufacturer 	= 'appl';
3021			clkDesc.componentFlags 			= 0L;
3022			clkDesc.componentFlagsMask 		= 0L;
3023
3024			deviceDescriptionPtr->clock = 0;
3025			deviceDescriptionPtr->clock = FindNextComponent( deviceDescriptionPtr->clock, &clkDesc);	// Look for FireWire clock component
3026			FailMessage( deviceDescriptionPtr->clock == nil);
3027
3028			// create device description atom structure
3029			result = QTNewAtomContainer( &deviceDescriptionPtr->deviceContainer);
3030			FailWithVal( result != noErr, Exit, result);
3031
3032			// add a device atom
3033			result = QTInsertChild( deviceDescriptionPtr->deviceContainer, kParentAtomIsContainer, kIDHDeviceAtomType,
3034						(long) *deviceID, 0, 0, nil, &deviceAtom);
3035			FailWithVal( result != noErr, Exit, result);
3036
3037			// add the unique 64 bit FireWire GUID id to device atom
3038			result = QTInsertChild( deviceDescriptionPtr->deviceContainer, deviceAtom, kIDHUniqueIDType,
3039						0, 0, sizeof(CSRNodeUniqueID), &csrNodeUniqueID, nil);
3040			FailWithVal( result != noErr, Exit, result);
3041
3042			result = cameraNameLookup(deviceDescriptionPtr, cameraName);
3043			FailWithVal( result != noErr, Exit, result);
3044			result = QTInsertChild( deviceDescriptionPtr->deviceContainer, deviceAtom, kIDHNameAtomType,
3045						0, 0, cameraName[0] + 1, cameraName, nil);
3046
3047			// add the IDH unique id to device atom
3048			result = QTInsertChild( deviceDescriptionPtr->deviceContainer, deviceAtom, kIDHDeviceIDType,
3049						0, 0, sizeof(deviceDescriptionPtr->deviceID), &deviceDescriptionPtr->deviceID, nil);
3050			FailWithVal( result != noErr, Exit, result);
3051
3052			// create a device status structure and add it to the device atom
3053			deviceStatus.version 				= 1;
3054			deviceStatus.physicallyConnected 	= true;
3055			deviceStatus.readEnabled 			= false;
3056			deviceStatus.writeEnabled 			= false;
3057			deviceStatus.exclusiveAccess 		= false;
3058			deviceStatus.currentBandwidth 		= 1;
3059			deviceStatus.currentChannel 		= 0;
3060			deviceStatus.inputStandard			= 0;
3061			deviceStatus.deviceActive 			= false;
3062
3063			result = QTInsertChild( deviceDescriptionPtr->deviceContainer, deviceAtom, kDVDeviceInfo,
3064						0, 0, sizeof( IDHDeviceStatus), &deviceStatus, nil);
3065			FailWithVal( result != noErr, Exit, result);
3066
3067			// add isoch descriptions to structure
3068			result = QTInsertChild( deviceDescriptionPtr->deviceContainer, deviceAtom, kIDHIsochServiceAtomType,
3069						0, 0, 0, nil, &isocAtom);
3070			FailWithVal( result != noErr, Exit, result);
3071
3072			// add the configs to the isoc atom
3073			result = setupVideoAtoms( deviceDescriptionPtr->deviceContainer, isocAtom, standard);
3074			FailWithVal( result != noErr, Exit, result);
3075
3076			if( standard == ntscIn)
3077			{
3078				result = setup48kAudioAtoms( deviceDescriptionPtr->deviceContainer, isocAtom, standard);
3079				FailWithVal( result != noErr, Exit, result);
3080
3081				result = setup32kAudioAtoms( deviceDescriptionPtr->deviceContainer, isocAtom, standard);
3082				FailWithVal( result != noErr, Exit, result);
3083			}
3084			else	// PAL audio
3085			{
3086				result = setup44kAudioAtoms( deviceDescriptionPtr->deviceContainer, isocAtom, standard);
3087				FailWithVal( result != noErr, Exit, result);
3088			}
3089		}
3090	}
3091	else	// process device disabled
3092	{
3093		RecordEventLogger( 'isoc', 'updt', 'disa', 0);
3094
3095		for( devIndex=0; devIndex < gGlobals->nDevices; ++devIndex)	// find disabled device in list
3096		{
3097			if (gGlobals->deviceDescription[devIndex].refNum == pGDFData->driverRefNum)	// do we match?
3098			{
3099				DeviceDescription 	*deviceDescriptionPtr = &(gGlobals->deviceDescription[devIndex]);
3100				int					i;
3101
3102				gGlobals->deviceDescription[devIndex].active = false;
3103
3104				*deviceID = gGlobals->deviceDescription[devIndex].deviceID;
3105
3106				RecordEventLogger( 'isoc', 'updt', 'disa', 'foun');
3107
3108				// find all of instances that have pending I/O for this device
3109				for( i=0; i<kMaxInstances; ++i)
3110				{
3111					IsochComponentInstancePtr 	ih = deviceDescriptionPtr->instancePtr[i];
3112					IDHParameterBlock 			*writeRequest, *readRequest;
3113
3114					if( deviceDescriptionPtr->instancePtr[i] == nil)	// no instance?
3115						continue;
3116
3117					// get a read request
3118					while( PBDequeueFirst( ih->readQueue, (QElemPtr *) &readRequest) == noErr)
3119					{
3120						RecordEventLogger( 'isoc', 'updt', 'disa', 'disr');
3121
3122						readRequest->result = kIDHErrDeviceDisconnected;
3123
3124						// call the client callback
3125						result = (*readRequest->completionProc)( (IDHGenericEvent *) readRequest, readRequest->refCon);
3126					}
3127
3128					// get a read request
3129					while( PBDequeueFirst( ih->writeQueue, (QElemPtr *) &writeRequest) == noErr)
3130					{
3131						RecordEventLogger( 'isoc', 'updt', 'disa', 'disw');
3132
3133						writeRequest->result = kIDHErrDeviceDisconnected;
3134
3135						// call the client callback
3136						result = (*writeRequest->completionProc)( (IDHGenericEvent *) writeRequest, writeRequest->refCon);
3137					}
3138				} // end of processing instances
3139
3140				break;
3141			}
3142		}
3143	}
3144
3145Exit:
3146	return result;
3147}
3148
3149
3150OSStatus writeNotifyProc( IDHGenericEvent *event, void *userData)
3151{
3152	OSErr 						result = noErr;
3153	IsochComponentGlobalsPtr	gGlobals = userData;
3154	DeviceDescription			*deviceDescriptionPtr;
3155	short						i;
3156
3157	RecordEventLogger( 'isoc', 'writ', 'comp', 'lete');
3158
3159	FailIf( event->eventHeader.event != kIDHPrivateEventWriteComplete, Exit);	// make sure this is a write
3160
3161	deviceDescriptionPtr = nil;	// find device causing notification in list
3162	for( i=0; i<kMaxDevicesActive; ++i)
3163	{
3164		if( gGlobals->deviceDescription[i].deviceID == event->eventHeader.deviceID)
3165		{
3166			deviceDescriptionPtr = &gGlobals->deviceDescription[i];
3167			break;
3168		}
3169	}
3170
3171	FailIf( deviceDescriptionPtr == nil, Exit);	// couldn't find device in list
3172
3173	for( i=0; i<kMaxInstances; ++i)	// process each client for this device
3174	{
3175		IsochComponentInstancePtr	ih = deviceDescriptionPtr->instancePtr[i];
3176		IDHParameterBlock 			*writeRequest;
3177		Ptr							tmpBuffPtr;
3178		unsigned long				frameSize;
3179
3180		if( deviceDescriptionPtr->instancePtr[i] == nil)	// no instance? Skip to next.
3181			continue;
3182
3183		if( PBDequeueFirst( ih->writeQueue, (QElemPtr *) &writeRequest) != noErr)	// get a write request
3184			continue;
3185
3186		// Get a buffer for output
3187		result = getEmptyOutputFrame( ih, &tmpBuffPtr, &frameSize);
3188		FailWithVal( result != noErr, Exit, result);
3189
3190		writeRequest->actualCount = 0;
3191		writeRequest->result = noErr;
3192
3193		FailMessage( frameSize < writeRequest->requestedCount)	// make sure buffer is big enough
3194		if( frameSize < writeRequest->requestedCount)
3195			result = kIDHErrDeviceWriteError;
3196
3197		if( result == noErr)	// valid frame avail?
3198		{
3199			if( writeRequest->buffer == nil)	// user wants buffer
3200			{
3201				writeRequest->buffer = tmpBuffPtr;	// pass the buffer back to the client
3202
3203				// call clients callback so that he can copy his data into buffer
3204				result = (*writeRequest->completionProc)( (IDHGenericEvent *) writeRequest, writeRequest->refCon);
3205				FailWithVal( result != noErr, Exit, result);
3206
3207				// write the frame
3208				result = writeFrame( ih, tmpBuffPtr);
3209				FailWithVal( result != noErr, Exit, result);
3210			}
3211			else // client supplied frame
3212			{
3213				// move the client data into the buffer
3214				BlockMoveData( writeRequest->buffer, tmpBuffPtr, writeRequest->requestedCount);
3215				writeRequest->actualCount = writeRequest->requestedCount;
3216
3217				// write the frame
3218				result = writeFrame( ih, tmpBuffPtr);
3219				FailWithVal( result != noErr, Exit, result);
3220
3221				// save the result and call clients callback
3222				writeRequest->result = result;
3223				result = (*writeRequest->completionProc)( (IDHGenericEvent *) writeRequest, writeRequest->refCon);
3224				FailMessage( result != noErr);
3225			}
3226
3227		}	// valid frame avail?
3228
3229	}	// next client
3230
3231	// reset callback
3232	result = IDHNotifyMeWhen( (ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr->writeIsochNotificationID,
3233				kIDHPrivateEventWriteComplete);
3234
3235Exit:
3236	return result;
3237}
3238
3239
3240OSStatus cameraNameLookup(DeviceDescriptionPtr pDeviceDescription, UInt8 *name)
3241{
3242	Handle		h;
3243	FSSpec		fileSpec;
3244	Str255		cameraName = "\pDV";
3245	SInt32		dirID;
3246	UInt32		*pGuid;
3247	UInt32		guidCount;
3248	UInt32		i, index;
3249	OSStatus	result = noErr;
3250	SInt16		refNum;
3251	Boolean		foundName = false;
3252
3253	BlockMoveData(cameraName, name, cameraName[0] + 1);
3254
3255	// first look for a "DV Names" preferences file
3256	// find the preferences folder
3257	result = FindFolder(kOnSystemDisk, kPreferencesFolderType, kDontCreateFolder, &refNum, &dirID);
3258	if (result != noErr)
3259		return noErr;
3260
3261	result = FSMakeFSSpec(refNum, dirID, "\pDV Names", &fileSpec);
3262	if (result == noErr)
3263	{
3264		// prefs file exsits, open it
3265		refNum = FSpOpenResFile(&fileSpec, fsRdPerm);
3266		result = ResError();
3267	}
3268
3269	if (result == noErr)
3270	{
3271		// read guid resource and look for matching guid
3272		h = Get1Resource('guid', -20775); // jkl, get resource assignment and constant
3273		result = ResError();
3274		if (h && (result == noErr))
3275		{
3276			HLock(h);
3277
3278			// first long is number of guid's stored in resource
3279			pGuid = (UInt32 *) *h;
3280			guidCount = *pGuid;
3281			pGuid++;
3282
3283			for (i = 0, index = 1; i < (guidCount * 2); i+=2, index++)
3284			{
3285				if ((pGuid[i] == pDeviceDescription->uniqueID.hi) && (pGuid[i+1] == pDeviceDescription->uniqueID.lo))
3286				{
3287					// found device, get its name
3288					GetIndString(cameraName, -20775, index);
3289					if (cameraName[0])
3290					{
3291						BlockMoveData(cameraName, name, cameraName[0] + 1);
3292						foundName = true;
3293						break;
3294					}
3295				}
3296			}
3297			HUnlock(h);
3298			ReleaseResource(h);
3299		}
3300
3301		CloseResFile(refNum);
3302	}
3303
3304	if (!foundName)
3305	{
3306		// look in local vendor ID resource stored in QT FW DV Support to at least determine manufacturer name
3307		result = FindFolder(kOnSystemDisk, kExtensionFolderType, kDontCreateFolder, &refNum, &dirID);
3308		if (result != noErr)
3309			return noErr;
3310	}
3311
3312	// jkl, this should be changed to search by type and creator, not name
3313	result = FSMakeFSSpec(refNum, dirID, "\pQuickTime FireWire DV Support", &fileSpec);
3314	if (result == noErr)
3315	{
3316		// prefs file exsits, open it
3317		refNum = FSpOpenResFile(&fileSpec, fsRdPerm);
3318		result = ResError();
3319		if (result != noErr)
3320			return noErr;
3321	}
3322
3323	if (result == noErr)
3324	{
3325		// read vendor id resource and look for matching guid
3326		h = Get1Resource('vnid', -20775); // jkl, get resource assignment and constant
3327		result = ResError();
3328		if (h && (result == noErr))
3329		{
3330			HLock(h);
3331
3332			// first long is number of vendor id's stored in resource
3333			guidCount = **((UInt32 **) h);
3334
3335			pGuid = *((UInt32 **) h);
3336			pGuid++;
3337
3338			for (i = 0, index = 1; i < guidCount; i++, index++)
3339			{
3340				if (pGuid[i] == (pDeviceDescription->uniqueID.hi >> 8))
3341				{
3342					// found device, get its name
3343					GetIndString(cameraName, -20775, index);
3344					if (cameraName[0])
3345					{
3346						BlockMoveData(cameraName, name, cameraName[0] + 1);
3347						BlockMoveData(" DV", name + name[0] + 1, 3);
3348						name[0] += 3;
3349						break;
3350					}
3351				}
3352			}
3353			HUnlock(h);
3354			ReleaseResource(h);
3355		}
3356
3357		CloseResFile(refNum);
3358	}
3359
3360	// don't fail because can't find a name
3361	return noErr;
3362}
3363
3364
3365struct UniqueIDAndName {
3366	CSRNodeUniqueID		guid;
3367	Str255				name;
3368};
3369typedef struct UniqueIDAndName UniqueIDAndName, *UniqueIDAndNamePtr;
3370
3371// structural representation of preferences file data
3372struct CameraNamePrefs {
3373	UInt32				idNameCount;
3374	UniqueIDAndName		idName[];
3375};
3376typedef struct CameraNamePrefs CameraNamePrefs, *CameraNamePrefsPtr;
3377
3378OSStatus updateCameraName(CSRNodeUniqueID *guid, UInt8 *name)
3379{
3380	UniqueIDAndNamePtr	pGuidName;
3381	FSSpec		fileSpec;
3382	SInt32		dirID;
3383	SInt32		requestedCount;
3384	UInt32		recordCount;
3385	UInt32		i;
3386	OSStatus	result = noErr;
3387	SInt16		refNum, vRefNum;
3388	Boolean		foundGuid = false;
3389	Boolean		fileOpened = false;
3390
3391	// look for a "DV Names" preferences file, jkl - name needs to be localized
3392	// find the preferences folder
3393	result = FindFolder(kOnSystemDisk, kPreferencesFolderType, kDontCreateFolder, &vRefNum, &dirID);
3394	if (result != noErr)
3395		return noErr;
3396
3397	result = FSMakeFSSpec(vRefNum, dirID, "\pDV Names", &fileSpec);
3398	if (result == fnfErr)
3399		result = FSpCreate(&fileSpec, 'dvcf', 'pref', smSystemScript);
3400
3401	if (result == noErr)
3402		result = FSpOpenDF(&fileSpec, fsRdWrPerm, &refNum);
3403
3404	if (result == noErr)
3405	{
3406		fileOpened = true;
3407		result = SetFPos(refNum, fsFromStart, 0);
3408	}
3409
3410	if (result == noErr)
3411	{
3412		requestedCount = 4;
3413		result = FSRead(refNum, &requestedCount, &recordCount);
3414	}
3415
3416	if ((result == noErr) && (requestedCount == 4))
3417	{
3418		requestedCount = recordCount * sizeof(UniqueIDAndName);
3419
3420		// allocate memory to read all of the data
3421		pGuidName = (UniqueIDAndNamePtr) NewPtr(requestedCount);
3422		if (pGuidName == nil)
3423			result = memFullErr;
3424
3425		if (result == noErr)
3426			result = FSRead(refNum, &requestedCount, pGuidName);
3427
3428		if (result == noErr)
3429		{
3430			for (i = 0; i < recordCount; i++)
3431			{
3432				if ((pGuidName[i].guid.hi == guid->hi) && (pGuidName[i].guid.lo == guid->lo))
3433				{
3434					BlockMoveData(name, pGuidName[i].name, name[0] + 1);
3435					foundGuid = true;
3436					break;
3437				}
3438			}
3439		}
3440	}
3441	else
3442	{
3443		// prefs file is empty
3444		result = noErr;
3445		recordCount = 1;
3446		requestedCount = sizeof(UniqueIDAndName);
3447
3448		// allocate memory for new record
3449		pGuidName = (UniqueIDAndNamePtr) NewPtr(requestedCount);
3450		if (pGuidName == nil)
3451			result = memFullErr;
3452
3453		if (result == noErr)
3454		{
3455			BlockMoveData(name, pGuidName[0].name, name[0] + 1);
3456			pGuidName[0].guid = *guid;
3457			foundGuid = true;
3458		}
3459	}
3460
3461	if ((result == noErr) && !foundGuid)
3462	{
3463		// allocate space for new record
3464		requestedCount = GetPtrSize((Ptr) pGuidName);
3465		requestedCount += sizeof(UniqueIDAndName);
3466		SetPtrSize((Ptr) pGuidName, requestedCount);
3467		result = MemError();
3468
3469		if (result == noErr)
3470		{
3471			BlockMoveData(name, pGuidName[recordCount].name, name[0] + 1);
3472			pGuidName[recordCount].guid = *guid;
3473		}
3474		recordCount++;
3475	}
3476
3477	if (result == noErr)
3478	{
3479		// write data back to disk
3480		result = SetFPos(refNum, fsFromStart, 0);
3481
3482		requestedCount = 4;
3483		if (result == noErr)
3484			result = FSWrite(refNum, &requestedCount, &recordCount);
3485
3486		requestedCount = GetPtrSize((Ptr) pGuidName);
3487		if (result == noErr)
3488			result = FSWrite(refNum, &requestedCount, pGuidName);
3489	}
3490
3491	if (fileOpened)
3492		FSClose(refNum);
3493
3494	FlushVol(nil, vRefNum);
3495
3496	return result;
3497}
3498
3499#pragma mark -
3500#pragma mark ���������� Firewire Calls  ����������
3501
3502OSStatus handleDeviceAdded(ComponentInstance ihc, GDFDeviceEventData *pGDFData)
3503{
3504	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
3505	IDHDeviceID					deviceID;
3506	OSStatus					status = noErr;
3507
3508	RecordEventLogger( 'isoc', 'hana', 0, 0); //���
3509
3510	// update component info
3511	status = updateDeviceList(ih, pGDFData, &deviceID, true /* activate */);
3512
3513	// Post deviceAddedEvent to anybody that wants one
3514	postEvent(ih->gGlobals, deviceID, kIDHEventDeviceAdded, nil);
3515
3516	return status;
3517}
3518
3519
3520OSStatus handleDeviceRemoved(ComponentInstance ihc, GDFDeviceEventData *pGDFData)
3521{
3522	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
3523	IDHDeviceID					deviceID;
3524	OSStatus					status = noErr;
3525
3526	RecordEventLogger( 'isoc', 'hanr', 0, 0);
3527
3528	// update component info
3529	status = updateDeviceList(ih, pGDFData, &deviceID, false /* deactivate */);
3530
3531	// Post deviceAddedEvent to anybody that wants one
3532	postEvent(ih->gGlobals, deviceID, kIDHEventDeviceRemoved, nil);
3533
3534	return status;
3535}
3536
3537
3538OSStatus initIsochPort(FWClientInitIsochPortParams *pInitIsochPortParams, UInt32* outCommandAcceptance)
3539{
3540	DeviceDescriptionPtr		pDeviceDescription;
3541	OSErr						error = noErr;
3542	Boolean						portIsTalker, trial;
3543	Boolean						portIsLocal = false;
3544
3545	pDeviceDescription = (DeviceDescriptionPtr) pInitIsochPortParams->fwClientIsochPortParams.refCon;
3546
3547	// Get port information
3548	portIsTalker = pInitIsochPortParams->fwClientIsochPortParams.portIsTalker;
3549	trial = pInitIsochPortParams->trial;
3550
3551	// if input and call is not for talker, then call is for local port
3552	if ((pDeviceDescription->inputIsochChannelID == pInitIsochPortParams->fwClientIsochPortParams.isochChannelID) && !portIsTalker)
3553		portIsLocal = true;
3554	// JKL, really could be an else here
3555	// if output and call is for talker, then call is for local port
3556	if ((pDeviceDescription->outputIsochChannelID == pInitIsochPortParams->fwClientIsochPortParams.isochChannelID) && portIsTalker)
3557		portIsLocal = true;
3558
3559	if (portIsTalker)
3560	{
3561		if (!trial)
3562		{
3563			if (portIsLocal)
3564				error = doLocalTalkerPort(pDeviceDescription, pInitIsochPortParams);
3565			else
3566				error = doRemoteTalkerPort(pDeviceDescription, pInitIsochPortParams);
3567		}
3568		else
3569		{
3570			if (portIsLocal)
3571				error = trialLocalTalkerPort(pDeviceDescription, pInitIsochPortParams);
3572			else
3573				error = trialRemoteTalkerPort(pDeviceDescription, pInitIsochPortParams);
3574		}
3575	}
3576	else
3577	{
3578		if (!trial)
3579		{
3580			if (portIsLocal)
3581				error = doLocalListenerPort(pDeviceDescription, pInitIsochPortParams);
3582			else
3583				error = doRemoteListenerPort(pDeviceDescription, pInitIsochPortParams);
3584		}
3585		else
3586		{
3587			if (portIsLocal)
3588				error = trialLocalListenerPort(pDeviceDescription, pInitIsochPortParams);
3589			else
3590				error = trialRemoteListenerPort(pDeviceDescription, pInitIsochPortParams);
3591		}
3592	}
3593
3594	FailMessageVal( error != noErr, error);
3595
3596	FWClientCommandIsComplete(
3597			pInitIsochPortParams->fwClientInterfaceParams.fwClientCommandID,
3598			error);
3599
3600	*outCommandAcceptance = kFWClientCommandAcceptNoMore;
3601
3602	return error;
3603}
3604
3605
3606OSStatus doLocalTalkerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams)
3607{
3608	OSStatus error = noErr;
3609
3610	error = FWAllocateIsochPortID(&pDeviceDescription->isochPortID, pDeviceDescription->dclProgramID,
3611								  pInitIsochPortParams->channelNum, pInitIsochPortParams->speed, true);
3612	FailMessageVal( error != noErr, error);
3613
3614	// Send an allocate isoch port command to allocate port for talking
3615	if (error == noErr)
3616	{
3617		FWSetFWCommandParams(pDeviceDescription->isochPortCommandObjectID, pDeviceDescription->fwClientID, kFWCommandSyncFlag, nil, 0);
3618		FWSetIsochPortCommandIsochPortID(pDeviceDescription->isochPortCommandObjectID, pDeviceDescription->isochPortID);
3619		error = FWAllocateLocalIsochronousPort(pDeviceDescription->isochPortCommandObjectID);
3620		FailMessageVal( error != noErr, error);
3621	}
3622
3623	if (pDeviceDescription->componentGlobals->useCMP)
3624	{
3625		if (error == noErr)
3626			error = cmpNewPointToPointConnection(4 * pDeviceDescription->pcrIndex,
3627												 pDeviceDescription->localFWReferenceID,
3628												 pInitIsochPortParams->channelNum,
3629												 pInitIsochPortParams->speed,
3630												 pDeviceDescription->cmpOverheadID,
3631												 pDeviceDescription);
3632	}
3633
3634	return error;
3635}
3636
3637
3638OSStatus doRemoteTalkerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams)
3639{
3640	OSStatus error = noErr;
3641
3642	if (pDeviceDescription->componentGlobals->useCMP)
3643	{
3644		// set camera's output plug
3645		if (error == noErr)
3646			error = cmpNewPointToPointConnection(0x04, pDeviceDescription->fwClientID, pInitIsochPortParams->channelNum,
3647												 pInitIsochPortParams->speed, pDeviceDescription->cmpOverheadID, pDeviceDescription);
3648	}
3649
3650	return error;
3651}
3652
3653
3654OSStatus trialLocalTalkerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams)
3655{
3656	UInt32		localOutputPCRValue, pcrValue;
3657	UInt32		fwBandwidthUnits;
3658	UInt32		channelNum;
3659	OSStatus	error = noErr;
3660
3661	if (pDeviceDescription->componentGlobals->useCMP)
3662	{
3663		error = cmpRead(4 * pDeviceDescription->pcrIndex, pDeviceDescription->localFWReferenceID, &pcrValue, pDeviceDescription);
3664
3665		if (error == noErr)
3666		{
3667			// initialize mac output plug, turns on connected bit, 448 overhead unlike cameras so two streams fit in started 0x1333 bandwidth, 122 quadlet payload
3668			// jkl, need to computer overhead, ask Eric
3669			pDeviceDescription->cmpOverheadID = 0x0c;
3670			localOutputPCRValue = 0x8000007a | (pDeviceDescription->cmpOverheadID << 10);
3671			error = cmpWrite(4 * pDeviceDescription->pcrIndex, pDeviceDescription->localFWReferenceID,
3672							 pcrValue, localOutputPCRValue, pDeviceDescription);
3673		}
3674
3675		if (error == noErr)
3676		{
3677			// reserve bandwidth
3678			// jkl, problem with this is final speed may be faster than s100, allocated bandwidth will be too much and not enough bandwidth will get deallocated
3679			// could either stay at default s100 or fix up the bandwidth in the commit phase
3680			fwBandwidthUnits = irmCalculateBandwidthUnits(localOutputPCRValue);
3681			error = irmAllocateBandwidth(pDeviceDescription->fwClientID, fwBandwidthUnits, pDeviceDescription);
3682		}
3683
3684		// reserve channel
3685		if (error == noErr)
3686		{
3687			pDeviceDescription->cmpBandwidth = fwBandwidthUnits;
3688
3689			channelNum = 0;
3690
3691			// reserve channel
3692			// jkl, what if further init stuff fails - like target device in camera mode, how do we know to release resources
3693			error = irmAllocateChannel(pDeviceDescription->fwClientID, &channelNum, pDeviceDescription);
3694
3695			if (error == noErr)
3696			{
3697				pDeviceDescription->cmpChannel = channelNum;
3698
3699				if (channelNum < 32)
3700				{
3701					pInitIsochPortParams->supportedChannelNumHi = ((UInt32) 0x80000000) >> channelNum;
3702					pInitIsochPortParams->supportedChannelNumLo = 0;
3703				}
3704				else
3705				{
3706					channelNum -= 32;
3707					pInitIsochPortParams->supportedChannelNumHi = 0;
3708					pInitIsochPortParams->supportedChannelNumLo = ((UInt32) 0x80000000) >> channelNum;
3709				}
3710			}
3711		}
3712
3713		// set speed
3714		if (error == noErr)
3715		{
3716			UInt32		localOutputMasterPlugValue;
3717
3718			error = cmpRead(0x00, pDeviceDescription->localFWReferenceID, &localOutputMasterPlugValue, pDeviceDescription);
3719			if (error == noErr)
3720				pInitIsochPortParams->speed = (localOutputMasterPlugValue >> 30) & 3;
3721			else
3722			{
3723				pInitIsochPortParams->speed = kFWSpeed100MBit;
3724
3725				// don't fail for this
3726				error = noErr;
3727			}
3728		}
3729
3730	}
3731	else
3732	{
3733		// no CMP just use channel 63, should make sure bandwidth is allocated
3734		pInitIsochPortParams->supportedChannelNumHi = 0;
3735		pInitIsochPortParams->supportedChannelNumLo = 1;
3736
3737		// set speed
3738		pInitIsochPortParams->speed = kFWSpeed100MBit;
3739	}
3740
3741	return error;
3742}
3743
3744
3745OSStatus trialRemoteTalkerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams)
3746{
3747	UInt32		fwBandwidthUnits;
3748	UInt32		channelNum;
3749	UInt32		pcrValue;
3750	OSStatus	error = noErr;
3751
3752	if (pDeviceDescription->componentGlobals->useCMP)
3753	{
3754		// determine current state of device
3755		error = cmpRead(0x04, pDeviceDescription->fwClientID, &pcrValue, pDeviceDescription);
3756		if (error == noErr)
3757		{
3758			// save channelNum
3759			channelNum = (pcrValue >> 16) & 0x3f;
3760
3761			// if there is a point to point connection or if camera is broadcasting on a channel other than 63,
3762			// use existing channel and bandwidth
3763			if ((pcrValue & 0x3f000000) || (((pcrValue & 0xC0000000) == 0xC0000000) && (channelNum != 63)))
3764			{
3765				if (channelNum < 32)
3766				{
3767					pInitIsochPortParams->supportedChannelNumHi = ((UInt32) 0x80000000) >> channelNum;
3768					pInitIsochPortParams->supportedChannelNumLo = 0;
3769				}
3770				else
3771				{
3772					channelNum -= 32;
3773					pInitIsochPortParams->supportedChannelNumHi = 0;
3774					pInitIsochPortParams->supportedChannelNumLo = ((UInt32) 0x80000000) >> channelNum;
3775				}
3776
3777				pDeviceDescription->cmpChannel = channelNum;
3778				pDeviceDescription->cmpBandwidth = irmCalculateBandwidthUnits(pcrValue);
3779				pDeviceDescription->cmpOverheadID = (pcrValue >> 10) & 0x0f;
3780			}
3781			else
3782			{
3783				if ((pcrValue & 0xC0000000) == 0xC0000000)
3784				{
3785					// device is broadcasting on 63, clear broadcast, release channel and bandwidth
3786					error = cmpWrite(0x04, pDeviceDescription->fwClientID, pcrValue, pcrValue & 0xbfffffff, pDeviceDescription);
3787					if (error == noErr)
3788						error = irmReleaseChannel(pDeviceDescription->fwClientID, 63, pDeviceDescription);
3789					if (error == noErr)
3790					{
3791						fwBandwidthUnits = irmCalculateBandwidthUnits(pcrValue);
3792						error = irmReleaseBandwidth(pDeviceDescription->fwClientID, fwBandwidthUnits, pDeviceDescription);
3793					}
3794				}
3795
3796				// allocate bandwidth
3797				// jkl, bandwidth could be wrong if speed is negotiated faster than s100, too much is allocated and not enough gets deallocated
3798				// could just hard wire to s100 or re-check bandwith in commit phase
3799				pDeviceDescription->cmpOverheadID = 0x0c;
3800				fwBandwidthUnits = irmCalculateBandwidthUnits((pcrValue & 0x03ff) | (pDeviceDescription->cmpOverheadID << 10));
3801				error = irmAllocateBandwidth(pDeviceDescription->fwClientID, fwBandwidthUnits, pDeviceDescription);
3802
3803				if (error == noErr)
3804				{
3805					pDeviceDescription->cmpBandwidth = fwBandwidthUnits;
3806
3807					// allocate channel
3808					channelNum = 0;
3809					error = irmAllocateChannel(pDeviceDescription->fwClientID, &channelNum, pDeviceDescription);
3810					if (error == noErr)
3811					{
3812						pDeviceDescription->cmpChannel = channelNum;
3813
3814						if (channelNum < 32)
3815						{
3816							pInitIsochPortParams->supportedChannelNumHi = ((UInt32) 0x80000000) >> channelNum;
3817							pInitIsochPortParams->supportedChannelNumLo = 0;
3818						}
3819						else
3820						{
3821							channelNum -= 32;
3822							pInitIsochPortParams->supportedChannelNumHi = 0;
3823							pInitIsochPortParams->supportedChannelNumLo = ((UInt32) 0x80000000) >> channelNum;
3824						}
3825					}
3826				}
3827			}
3828		}
3829
3830		// set speed
3831		if (error == noErr)
3832		{
3833			UInt32		remoteOutputMasterPlugValue;
3834
3835			error = cmpRead(0x00, pDeviceDescription->fwClientID, &remoteOutputMasterPlugValue, pDeviceDescription);
3836			if (error == noErr)
3837				pInitIsochPortParams->speed = (remoteOutputMasterPlugValue >> 30) & 3;
3838			else
3839			{
3840				pInitIsochPortParams->speed = kFWSpeed100MBit;
3841
3842				// don't fail for this
3843				error = noErr;
3844			}
3845		}
3846
3847	}
3848	else
3849	{
3850		// just use channel 63
3851		pInitIsochPortParams->supportedChannelNumHi = 0;
3852		pInitIsochPortParams->supportedChannelNumLo = 1;
3853
3854		// set speed
3855		pInitIsochPortParams->speed = kFWSpeed100MBit;
3856	}
3857
3858	return error;
3859}
3860
3861
3862OSStatus doLocalListenerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams)
3863{
3864	OSStatus error = noErr;
3865
3866	error = FWAllocateIsochPortID(&pDeviceDescription->isochPortID, pDeviceDescription->dclProgramID,
3867								  pInitIsochPortParams->channelNum, pInitIsochPortParams->speed, false);
3868	FailMessageVal( error != noErr, error);
3869
3870	// Send an allocate isoch port command to allocate port for listening
3871	if (error == noErr)
3872	{
3873		FWSetFWCommandParams(pDeviceDescription->isochPortCommandObjectID, pDeviceDescription->fwClientID, kFWCommandSyncFlag, nil, 0);
3874		FWSetIsochPortCommandIsochPortID(pDeviceDescription->isochPortCommandObjectID, pDeviceDescription->isochPortID);
3875		error = FWAllocateLocalIsochronousPort(pDeviceDescription->isochPortCommandObjectID);
3876		FailMessageVal( error != noErr, error);
3877	}
3878
3879	if (pDeviceDescription->componentGlobals->useCMP)
3880	{
3881		// set mac's input plug
3882		if (error == noErr)
3883			error = cmpNewPointToPointConnection(0x80 + (4 * pDeviceDescription->pcrIndex),
3884												 pDeviceDescription->localFWReferenceID,
3885												 pInitIsochPortParams->channelNum, 0, 0, pDeviceDescription);
3886	}
3887
3888	return error;
3889}
3890
3891
3892OSStatus doRemoteListenerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams)
3893{
3894	OSStatus error = noErr;
3895
3896	if (pDeviceDescription->componentGlobals->useCMP)
3897	{
3898		if (error == noErr)
3899		{
3900			// set camera's input plug
3901			error = cmpNewPointToPointConnection(0x84, pDeviceDescription->fwClientID, pInitIsochPortParams->channelNum, 0, 0, pDeviceDescription);
3902		}
3903	}
3904
3905	return error;
3906}
3907
3908
3909OSStatus trialLocalListenerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams)
3910{
3911	UInt32		pcrValue;
3912	OSStatus	error = noErr;
3913
3914	if (pDeviceDescription->componentGlobals->useCMP)
3915	{
3916		// initialize mac plug, on-line, read value should be 0
3917		error = cmpRead(4 * pDeviceDescription->pcrIndex, pDeviceDescription->localFWReferenceID, &pcrValue, pDeviceDescription);
3918		if (error == noErr)
3919			error = cmpWrite(4 * pDeviceDescription->pcrIndex, pDeviceDescription->localFWReferenceID,
3920							 pcrValue, 0x80000000, pDeviceDescription);
3921
3922		if (error == noErr)
3923			error = cmpRead(0x80, pDeviceDescription->localFWReferenceID, &pcrValue, pDeviceDescription);
3924
3925		if (error == noErr)
3926			pInitIsochPortParams->speed = (pcrValue >> 30) & 3;
3927		else
3928		{
3929			pInitIsochPortParams->speed = kFWSpeed100MBit;
3930
3931			// don't fail for this
3932			error = noErr;
3933		}
3934	}
3935	else
3936		pInitIsochPortParams->speed = kFWSpeed100MBit;
3937
3938	pInitIsochPortParams->supportedChannelNumHi = 0xffffffff;
3939	pInitIsochPortParams->supportedChannelNumLo = 0xffffffff;
3940
3941	return error;
3942}
3943
3944
3945OSStatus trialRemoteListenerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams)
3946{
3947	UInt32		remoteInputMasterPlugValue;
3948	OSStatus	error = noErr;
3949
3950	if (pDeviceDescription->componentGlobals->useCMP)
3951	{
3952		error = cmpRead(0x80, pDeviceDescription->fwClientID, &remoteInputMasterPlugValue, pDeviceDescription);
3953		if (error == noErr)
3954			pInitIsochPortParams->speed = (remoteInputMasterPlugValue >> 30) & 3;
3955		else
3956		{
3957			pInitIsochPortParams->speed = kFWSpeed100MBit;
3958
3959			// don't fail for this
3960			error = noErr;
3961		}
3962	}
3963	else
3964		pInitIsochPortParams->speed = kFWSpeed100MBit;
3965
3966	pInitIsochPortParams->supportedChannelNumHi = 0xffffffff;
3967	pInitIsochPortParams->supportedChannelNumLo = 0xffffffff;
3968
3969	return error;
3970}
3971
3972
3973OSStatus releaseIsochPort(FWClientReleaseIsochPortParams *pReleaseIsochPortParams, UInt32* outCommandAcceptance)
3974{
3975	DeviceDescriptionPtr		pDeviceDescription;
3976	FWReferenceID				fwClientID;
3977	Boolean						portIsTalker;
3978	Boolean						portIsLocal = false;
3979	OSErr						error = noErr;
3980
3981	pDeviceDescription = (DeviceDescriptionPtr) pReleaseIsochPortParams->fwClientIsochPortParams.refCon;
3982
3983	// Get port information
3984	fwClientID = pReleaseIsochPortParams->fwClientInterfaceParams.fwReferenceID;
3985	portIsTalker = pReleaseIsochPortParams->fwClientIsochPortParams.portIsTalker;
3986
3987	// if input and call is not for talker, then call is for local port
3988	if ((pDeviceDescription->inputIsochChannelID == pReleaseIsochPortParams->fwClientIsochPortParams.isochChannelID) && !portIsTalker)
3989		portIsLocal = true;
3990	// JKL, really could be an else here
3991	// if output and call is for talker, then call is for local port
3992	if ((pDeviceDescription->outputIsochChannelID == pReleaseIsochPortParams->fwClientIsochPortParams.isochChannelID) && portIsTalker)
3993		portIsLocal = true;
3994
3995	// if called for local port, release port from FSL
3996	if (portIsLocal)
3997	{
3998		FWSetFWCommandParams(pDeviceDescription->isochPortCommandObjectID, pDeviceDescription->fwClientID, kFWCommandSyncFlag, nil, 0);
3999		FWSetIsochPortCommandIsochPortID(pDeviceDescription->isochPortCommandObjectID, pDeviceDescription->isochPortID);
4000		FWReleaseLocalIsochronousPort(pDeviceDescription->isochPortCommandObjectID);
4001
4002		FWDeallocateIsochPortID(pDeviceDescription->isochPortID);
4003		pDeviceDescription->isochPortID = 0;
4004	}
4005
4006	if (pDeviceDescription->componentGlobals->useCMP)
4007	{
4008		if (portIsTalker)
4009		{
4010			if (portIsLocal)
4011				error = cmpDisposePointToPointConnection(pDeviceDescription->pcrIndex * 4, pDeviceDescription->localFWReferenceID, pDeviceDescription);
4012			else
4013				error = cmpDisposePointToPointConnection(0x04, fwClientID, pDeviceDescription);
4014		}
4015		else
4016		{
4017			if (portIsLocal)
4018				error = cmpDisposePointToPointConnection(0x80 + (pDeviceDescription->pcrIndex * 4), pDeviceDescription->localFWReferenceID, pDeviceDescription);
4019			else
4020				error = cmpDisposePointToPointConnection(0x84, fwClientID, pDeviceDescription);
4021		}
4022	}
4023
4024	FWClientCommandIsComplete(
4025			pReleaseIsochPortParams->fwClientInterfaceParams.fwClientCommandID,
4026			error);
4027
4028	*outCommandAcceptance = kFWClientCommandAcceptNoMore;
4029
4030	return error;
4031}
4032
4033
4034OSStatus startIsochPort(FWClientIsochPortControlParams *pIsochPortControlParams, UInt32* outCommandAcceptance)
4035{
4036	DeviceDescriptionPtr		pDeviceDescription;
4037	FWClientCommandID			fwClientCommandID;
4038	FWReferenceID				fwClientID;
4039	Boolean						completionIsPending = false;
4040	Boolean						portIsTalker;
4041	Boolean						portIsLocal = false;
4042	OSErr						error = noErr;
4043
4044	RecordEventLogger( 'isoc', 'strt', 'port', 'bgn ');
4045
4046	pDeviceDescription = (DeviceDescriptionPtr) pIsochPortControlParams->fwClientIsochPortParams.refCon;
4047
4048	// Get port information
4049	fwClientID = pIsochPortControlParams->fwClientInterfaceParams.fwReferenceID;
4050	portIsTalker = pIsochPortControlParams->fwClientIsochPortParams.portIsTalker;
4051
4052	// if input and call is not for talker, then call is for local port
4053	if ((pDeviceDescription->inputIsochChannelID == pIsochPortControlParams->fwClientIsochPortParams.isochChannelID) && !portIsTalker)
4054		portIsLocal = true;
4055	// JKL, really could be an else here
4056	// if output and call is for talker, then call is for local port
4057	if ((pDeviceDescription->outputIsochChannelID == pIsochPortControlParams->fwClientIsochPortParams.isochChannelID) && portIsTalker)
4058		portIsLocal = true;
4059
4060	// if called for local port, start port
4061	if (portIsLocal)
4062	{
4063		fwClientCommandID = pIsochPortControlParams->fwClientInterfaceParams.fwClientCommandID;
4064		FWSetFWCommandParams(pDeviceDescription->startIsochPortCommandObjectID, pDeviceDescription->fwClientID, 0, isochPortCommandCompletionProc, (UInt32) fwClientCommandID);
4065		FWSetIsochPortCommandIsochPortID(pDeviceDescription->startIsochPortCommandObjectID, pDeviceDescription->isochPortID);
4066		error = FWStartLocalIsochronousPort(pDeviceDescription->startIsochPortCommandObjectID);
4067		FailMessageVal( error != noErr, error);
4068		completionIsPending = true; //zzz what if above call returns an error?
4069	}
4070
4071	if ((error == noErr) && completionIsPending)
4072		error = kIDHErrCompletionPending;
4073
4074	if (error != kIDHErrCompletionPending)
4075	{
4076		FWClientCommandIsComplete(
4077				pIsochPortControlParams->fwClientInterfaceParams.fwClientCommandID,
4078				error);
4079	}
4080	else
4081	{
4082		error = noErr;
4083	}
4084
4085	*outCommandAcceptance = kFWClientCommandAcceptNoMore;
4086
4087	return error;
4088}
4089
4090
4091OSStatus stopIsochPort(FWClientIsochPortControlParams *pIsochPortControlParams, UInt32* outCommandAcceptance)
4092{
4093	DeviceDescriptionPtr		pDeviceDescription;
4094	FWClientCommandID			fwClientCommandID;
4095	FWReferenceID				fwClientID;
4096	Boolean						completionIsPending = false;
4097	Boolean						portIsTalker;
4098	Boolean						portIsLocal = false;
4099	OSErr						error = noErr;
4100
4101	pDeviceDescription = (DeviceDescriptionPtr) pIsochPortControlParams->fwClientIsochPortParams.refCon;
4102
4103	// Get port information
4104	fwClientID = pIsochPortControlParams->fwClientInterfaceParams.fwReferenceID;
4105	portIsTalker = pIsochPortControlParams->fwClientIsochPortParams.portIsTalker;
4106
4107	// if input and call is not for talker, then call is for local port
4108	if ((pDeviceDescription->inputIsochChannelID == pIsochPortControlParams->fwClientIsochPortParams.isochChannelID) && !portIsTalker)
4109		portIsLocal = true;
4110	// JKL, really could be an else here
4111	// if output and call is for talker, then call is for local port
4112	if ((pDeviceDescription->outputIsochChannelID == pIsochPortControlParams->fwClientIsochPortParams.isochChannelID) && portIsTalker)
4113		portIsLocal = true;
4114
4115	// if called for local port, stop port
4116	if (portIsLocal)
4117	{
4118		fwClientCommandID = pIsochPortControlParams->fwClientInterfaceParams.fwClientCommandID;
4119		FWSetFWCommandParams(pDeviceDescription->stopIsochPortCommandObjectID, pDeviceDescription->fwClientID, 0, isochPortCommandCompletionProc, (UInt32) fwClientCommandID);
4120		FWSetIsochPortCommandIsochPortID(pDeviceDescription->stopIsochPortCommandObjectID, pDeviceDescription->isochPortID);
4121		error = FWStopLocalIsochronousPort(pDeviceDescription->stopIsochPortCommandObjectID);
4122		FailMessageVal( error != noErr, error);
4123		completionIsPending = true; //zzz what if above call returns an error?
4124	}
4125
4126	if ((error == noErr) && completionIsPending)
4127		error = kIDHErrCompletionPending;
4128
4129	if (error != kIDHErrCompletionPending)
4130	{
4131		FWClientCommandIsComplete(
4132				pIsochPortControlParams->fwClientInterfaceParams.fwClientCommandID,
4133				error);
4134	}
4135	else
4136	{
4137		error = noErr;
4138	}
4139
4140	*outCommandAcceptance = kFWClientCommandAcceptNoMore;
4141
4142	return error;
4143}
4144
4145
4146void isochPortCommandCompletionProc(FWCommandObjectID fwCommandObjectID, OSStatus commandStatus, UInt32 completionProcData)
4147{
4148	FWClientCommandIsComplete((FWClientCommandID) completionProcData, commandStatus);
4149}
4150
4151////////////////////////////////////////////////////////////////////////////////
4152//
4153// cmpDisposePointToPointConnection
4154//
4155// Tear down a point to point connection through the client's plug control register.
4156//
4157// *** Must be task level only
4158OSErr cmpDisposePointToPointConnection (
4159	UInt32						pcrOffset,			// offset of register address from fffff0000900
4160	FWClientID					clientID,
4161	DeviceDescriptionPtr		pDeviceDescription)
4162{
4163	UInt32						pcrValue, expectedPCRValue;
4164	OSErr						err = noErr;
4165
4166	if (pcrOffset > 0x00fc)
4167		err = addressRangeErr;
4168
4169	// get existing plug control register value
4170	if (err == noErr)
4171		err = cmpRead(pcrOffset, clientID, &pcrValue, pDeviceDescription);
4172
4173	if (err == noErr)
4174	{
4175		expectedPCRValue = pcrValue;
4176
4177		// decrement point to point counter
4178		// jkl, only if it is already set, need to fix this
4179		if (pcrValue & 0x3f000000)
4180			pcrValue -= 0x01000000;
4181
4182		// if point to point goes to zero, also turn off broadcast
4183		// jkl, Sony suggests to also make sure camera is stopped
4184		if (!(pcrValue & 0x3f000000))
4185			pcrValue &= 0xbfffffff;
4186
4187		err = cmpWrite(pcrOffset, clientID, expectedPCRValue, pcrValue, pDeviceDescription);
4188
4189		// release isochronous resources if counter goes to zero and device was talking
4190		if (err == noErr)
4191		{
4192			if (!(pcrValue & 0x3f000000) && (pcrOffset < 0x080))
4193			{
4194				irmReleaseChannel(clientID, (pcrValue & 0x003f0000) >> 16, pDeviceDescription);
4195				irmReleaseBandwidth(clientID, irmCalculateBandwidthUnits(pcrValue), pDeviceDescription);
4196			}
4197		}
4198	}
4199
4200	return err;
4201}
4202
4203
4204////////////////////////////////////////////////////////////////////////////////
4205//
4206// cmpNewPointToPointConnection
4207//
4208// Create a point to point connection through the client's and the local
4209// plug control register.
4210//
4211// *** Must be task level only
4212OSErr cmpNewPointToPointConnection (
4213	UInt32						pcrOffset,			// offset of register address from fffff0000900
4214	FWClientID					clientID,
4215	UInt32						channel,
4216	UInt32						speed,
4217	UInt32						overhead,
4218	DeviceDescriptionPtr		pDeviceDescription)
4219{
4220	UInt32						pcrValue, expectedPCRValue;
4221	OSErr						err = noErr;
4222
4223	if (pcrOffset > 0x00fc)
4224		err = addressRangeErr;
4225
4226	// get existing plug control register value
4227	if (err == noErr)
4228		err = cmpRead(pcrOffset, clientID, &pcrValue, pDeviceDescription);
4229
4230	if (err == noErr)
4231	{
4232		expectedPCRValue = pcrValue;
4233
4234		// increment point to point counter, will this ever overflow?, not turning off broadcast
4235		pcrValue += 0x01000000;
4236
4237		if (pcrOffset < 0x80)
4238		{// setting up output plug
4239			// set speed
4240			pcrValue |= (speed << 14);
4241
4242			// set overhead
4243			pcrValue |= (overhead << 10);
4244
4245			// if not already broadcasting or if point-to-point counter was previously not 0, set channel
4246			if (((pcrValue & 0x3f000000) == 0x01000000) && !(pcrValue & 0x40000000))
4247			{
4248				// set channel number
4249				pcrValue &= 0xffc0ffff;
4250				pcrValue |= (channel << 16);
4251			}
4252		}
4253		else
4254		{
4255			// for input plug just set channel if no point-to-point exists
4256			if ((pcrValue & 0x3f000000) == 0x01000000)
4257			{
4258				// set channel number
4259				pcrValue &= 0xffc0ffff;
4260				pcrValue |= (channel << 16);
4261			}
4262		}
4263
4264		err = cmpWrite(pcrOffset, clientID, expectedPCRValue, pcrValue, pDeviceDescription);
4265	}
4266
4267	return err;
4268}
4269
4270
4271////////////////////////////////////////////////////////////////////////////////
4272//
4273// cmpRead
4274//
4275// Read the plug control register specified by the offset.
4276//
4277// *** Must be task level only
4278OSErr cmpRead (
4279	UInt32						pcrOffset,			// offset of register address from fffff0000900
4280	FWClientID					clientID,
4281	UInt32						*pcrValue,
4282	DeviceDescriptionPtr		deviceDescriptionPtr)
4283{
4284	FWCommandObjectID			fwCommandObjectID = nil;
4285	UInt32						retryCount;
4286	OSErr						err = noErr;
4287
4288	if (pcrOffset > 0x00fc)
4289		err = addressRangeErr;
4290
4291	fwCommandObjectID = deviceDescriptionPtr->asynchCommandObjectID;
4292
4293	// set command params for synchronous read
4294	if (err == noErr)
4295		err = FWSetFWCommandParams(fwCommandObjectID, clientID, kFWCommandSyncFlag, nil, 0);
4296
4297	// set more command params for synchronous read
4298	if (err == noErr)
4299		err = FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000900 + pcrOffset, (Ptr) pcrValue, 4);
4300
4301	if (err == noErr)
4302	{
4303		retryCount = 10;
4304		do
4305		{
4306			retryCount--;
4307			err = FWRead(fwCommandObjectID);
4308			if (err == noErr)
4309				return err;
4310
4311			// don't slam the camera too hard
4312			DelayForHardware(DurationToAbsolute(1 * durationMillisecond));
4313		}
4314		// probably should retry only for specific error like retryExceeded or timeOut
4315		while (err && retryCount);
4316
4317		err = retryExceededErr;
4318	}
4319
4320	return err;
4321}
4322
4323
4324////////////////////////////////////////////////////////////////////////////////
4325//
4326// cmpWrite
4327//
4328// Write a plug control register through a compare and swap lock transaction.
4329// The plug control register is specified by the offset. After the transaction
4330// The expectedPCRValue is set to the value of the PCR prior before the request.
4331// If this value equals the original passed in value the command completed.
4332//
4333// *** Must be task level only
4334
4335OSErr cmpWrite (
4336	UInt32						pcrOffset,			// offset of register address from fffff0000900
4337	FWClientID					clientID,
4338	UInt32						expectedPCRValue,
4339	UInt32						newPCRValue,
4340	DeviceDescriptionPtr		deviceDescriptionPtr)
4341{
4342	FWCommandObjectID			fwCommandObjectID = nil;
4343	UInt32						compareAndSwapBuffer[2];
4344	UInt32						retryCount;
4345	OSErr						err = noErr;
4346
4347	if (pcrOffset > 0x00fc)
4348		err = addressRangeErr;
4349
4350	fwCommandObjectID = deviceDescriptionPtr->asynchCommandObjectID;
4351
4352	// set command params for synchronous lock
4353	if (err == noErr)
4354		err = FWSetFWCommandParams(fwCommandObjectID, clientID, kFWCommandSyncFlag, nil, 0);
4355
4356	// set more command params for synchronous lock
4357	if (err == noErr)
4358	{
4359		compareAndSwapBuffer[0] = expectedPCRValue;
4360		compareAndSwapBuffer[1] = newPCRValue;
4361		err = FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000900 + pcrOffset, (Ptr) compareAndSwapBuffer, 8);
4362	}
4363
4364	retryCount = 10;
4365	do
4366	{
4367		retryCount--;
4368		// JKL, fix this, arguments need to be fixed up after each try
4369		err = FWCompareAndSwap(fwCommandObjectID);
4370		if (err == noErr)
4371		{
4372			if (compareAndSwapBuffer[0] == expectedPCRValue)
4373				return err;
4374			else
4375			{
4376				// force it to try again, although probably lost cause at this point
4377				// jkl, need to start back at the beginning of the write command to redo values
4378				err = retryExceededErr;
4379
4380				// jkl ***, hack, reset expectedPCRValue to what was returned even though this no longer may be a valid starting argument
4381				expectedPCRValue = compareAndSwapBuffer[0];
4382			}
4383		}
4384
4385		// don't slam the camera too hard
4386		DelayForHardware(DurationToAbsolute(1 * durationMillisecond));
4387	}
4388	// probably should retry only for specific error like retryExceeded or timeOut
4389	while (err && retryCount);
4390
4391	return err;
4392}
4393
4394
4395////////////////////////////////////////////////////////////////////////////////
4396//
4397// cmpHandleLocalLock
4398//
4399// This is a notification routine that is called once the response to a lock
4400// transaction to one of the PCR addresses has been sent. One way to handle
4401// this is to look at the result of the transaction, is it consistent with
4402// the current configuration. Does the configuration change to match the PCR
4403// request or is the PCR updated with the current configuration overriding
4404// the lock request. This may also be called for locks initiated by the driver
4405// to a local PCR.
4406//
4407// There is also a LockRequest notification that could be setup to look at the
4408// request. I don't know if we have the ability to override the lock request
4409// and basically deny it before FSL responds. Something to talk over with Eric
4410// and Collin.
4411//
4412OSStatus cmpHandleLocalLock(
4413	FWClientAsynchRequestParamsPtr	pFWClientAsynchRequestParams,
4414	UInt32							*pCommandAcceptance)
4415{
4416	DeviceDescriptionPtr pDeviceDescription;
4417	OSStatus status = noErr;
4418
4419	pDeviceDescription = (DeviceDescriptionPtr) pFWClientAsynchRequestParams->pAddressSpecificData;
4420
4421	// now what?
4422
4423	// Complete FireWire client command.
4424	FWClientCommandIsComplete(pFWClientAsynchRequestParams->fwClientInterfaceParams.fwClientCommandID, status);
4425	*pCommandAcceptance = kFWClientCommandAcceptNoMore;
4426
4427	return status;
4428}
4429
4430
4431// *** Must be task level only
4432OSStatus irmReleaseChannel(
4433	FWClientID					fwClientID,
4434	UInt32						channel,
4435	DeviceDescriptionPtr		deviceDescriptionPtr)
4436{
4437	FWReferenceID				fwIsochResourceManagerID;
4438	FWCommandObjectID			fwCommandObjectID = nil;
4439	UInt32						asynchCommandArgs[2];
4440	UInt32						clippedChannel, saveMaxRT;
4441	OSStatus					status = noErr;
4442
4443	status = FWGetFWIsochResourceManagerID(fwClientID, &fwIsochResourceManagerID);
4444
4445	if ((status == noErr) && (fwIsochResourceManagerID != (FWReferenceID) kInvalidFWIsochResourceManagerID))
4446	{
4447		fwCommandObjectID = deviceDescriptionPtr->asynchCommandObjectID;
4448
4449		FWSetFWCommandParams(fwCommandObjectID, fwIsochResourceManagerID, kFWCommandSyncFlag, nil, 0);
4450
4451		FWGetAsynchCommandMaxRetries ( fwCommandObjectID, &saveMaxRT );
4452		FWSetAsynchCommandMaxRetries ( fwCommandObjectID, kMaxRetries );
4453
4454		if (channel < 32)
4455			FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000224, (Ptr) &asynchCommandArgs[0], 4);
4456		else
4457			FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000228, (Ptr) &asynchCommandArgs[0], 4);
4458
4459		// read channel allocated register
4460		status = FWRead(fwCommandObjectID);
4461
4462		if (status == noErr)
4463		{
4464			// check if channel is reserved
4465			clippedChannel = (channel & 0x0000001f);
4466			if (!(asynchCommandArgs[0] & (0x80000000 >> clippedChannel)))
4467			{
4468				// channel is allocated, deallocate it
4469				asynchCommandArgs[1] = asynchCommandArgs[0] | (0x80000000 >> clippedChannel);
4470
4471				// use existing settings except for length
4472				FWSetAsynchCommandLength(fwCommandObjectID, 8);
4473
4474				status = FWCompareAndSwap(fwCommandObjectID);
4475			}
4476		}
4477		FWSetAsynchCommandMaxRetries ( fwCommandObjectID, saveMaxRT );
4478	}
4479
4480	return status;
4481}
4482
4483
4484// *** Must be task level only
4485OSStatus irmReleaseBandwidth(
4486	FWClientID					fwClientID,
4487	UInt32						fwBandwidthUnits,
4488	DeviceDescriptionPtr		deviceDescriptionPtr)
4489{
4490	FWReferenceID				fwIsochResourceManagerID;
4491	FWCommandObjectID			fwCommandObjectID = nil;
4492	UInt32						asynchCommandArgs[2], saveMaxRT;
4493	OSStatus					status = noErr;
4494
4495	status = FWGetFWIsochResourceManagerID(fwClientID, &fwIsochResourceManagerID);
4496
4497	if ((status == noErr) && (fwIsochResourceManagerID != (FWReferenceID) kInvalidFWIsochResourceManagerID))
4498	{
4499		// read IRM bandwidth register
4500		fwCommandObjectID = deviceDescriptionPtr->asynchCommandObjectID;
4501
4502		FWSetFWCommandParams(fwCommandObjectID, fwIsochResourceManagerID, kFWCommandSyncFlag, nil, 0);
4503
4504		FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000220, (Ptr) &asynchCommandArgs[0], 8);
4505
4506		FWGetAsynchCommandMaxRetries ( fwCommandObjectID, &saveMaxRT );
4507		FWSetAsynchCommandMaxRetries ( fwCommandObjectID, kMaxRetries );
4508
4509		// Set up arguments for add.
4510		// First parameter is the threshold, second is amount to add.
4511		// JKL *** Sony uses 0x1388 for starting available bandwidth which is nice since most of the
4512		// camcorders are allocating more bandwidth for one channel than necessary and not enough is
4513		// left for an additional channel
4514		// p1394 says IRM max bandwidth value should be 0x1333, 4915
4515		// asynchCommandArgs[0] = 0x1388;
4516		asynchCommandArgs[0] = 0x1333;
4517		asynchCommandArgs[1] = fwBandwidthUnits;
4518
4519		// Add to bandwidth allocation.
4520		status = FWClippedAdd(fwCommandObjectID);
4521
4522		FWSetAsynchCommandMaxRetries ( fwCommandObjectID, saveMaxRT );
4523	}
4524
4525	return status;
4526}
4527
4528
4529// *** Must be task level only
4530OSStatus irmAllocateChannel(
4531	FWClientID					fwClientID,
4532	UInt32						*channel,
4533	DeviceDescriptionPtr		deviceDescriptionPtr)
4534{
4535	FWReferenceID				fwIsochResourceManagerID;
4536	FWCommandObjectID			fwCommandObjectID = nil;
4537	UInt32						asynchCommandArgs[2], channelArgs, saveMaxRT;
4538	OSStatus					status = noErr;
4539
4540	status = FWGetFWIsochResourceManagerID(fwClientID, &fwIsochResourceManagerID);
4541
4542	// JKL *** this is not right since noErr could get returned but not have an IRM
4543	if ((status == noErr) && (fwIsochResourceManagerID != (FWReferenceID) kInvalidFWIsochResourceManagerID))
4544	{
4545		// read IRM channel register
4546		fwCommandObjectID = deviceDescriptionPtr->asynchCommandObjectID;
4547
4548		FWSetFWCommandParams(fwCommandObjectID, fwIsochResourceManagerID, kFWCommandSyncFlag, nil, 0);
4549
4550		// populate both command args with the channel registers
4551		FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000224, (Ptr) &asynchCommandArgs[0], 4);
4552
4553		FWGetAsynchCommandMaxRetries ( fwCommandObjectID, &saveMaxRT );
4554		FWSetAsynchCommandMaxRetries ( fwCommandObjectID, kMaxRetries );
4555
4556		// read channel hi allocated register
4557		status = FWRead(fwCommandObjectID);
4558
4559		if (status == noErr)
4560		{
4561			FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000228, (Ptr) &asynchCommandArgs[1], 4);
4562
4563			// read channel lo allocated register
4564			status = FWRead(fwCommandObjectID);
4565		}
4566
4567		if (status == noErr)
4568		{
4569			// get next available channel
4570			if (*channel < 32)
4571				channelArgs = asynchCommandArgs[0];
4572			else
4573				channelArgs = asynchCommandArgs[1];
4574			while (! (channelArgs & (0x80000000 >> (*channel & 0x0000001f))))
4575			{
4576				*channel = *channel + 1;
4577				if (*channel > 31)
4578					channelArgs = asynchCommandArgs[1];
4579
4580				if (*channel > 63)
4581					break;
4582			}
4583
4584			// hopefully a channel got allocated
4585			if (*channel < 32)
4586			{
4587				// set channel hi address
4588				FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000224, (Ptr) &asynchCommandArgs[0], 8);
4589			}
4590			else if (*channel < 64)
4591			{
4592				// set channel lo address
4593				FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000228, (Ptr) &asynchCommandArgs[0], 8);
4594			}
4595			else
4596				status = noChannelsAvailableErr;
4597
4598			if (status == noErr)
4599			{
4600				// clear allocated channel bit
4601				asynchCommandArgs[1] = asynchCommandArgs[0] & ~(0x80000000 >> (*channel & 0x01f));
4602
4603				// do the command
4604				status = FWCompareAndSwap(fwCommandObjectID);
4605			}
4606		}
4607
4608		FWSetAsynchCommandMaxRetries ( fwCommandObjectID, saveMaxRT );
4609	}
4610
4611	return status;
4612}
4613
4614
4615// *** Must be task level only
4616OSStatus irmAllocateBandwidth(
4617	FWClientID					fwClientID,
4618	UInt32						fwBandwidthUnits,
4619	DeviceDescriptionPtr		deviceDescriptionPtr)
4620{
4621	FWReferenceID				fwIsochResourceManagerID;
4622	FWCommandObjectID			fwCommandObjectID = nil;
4623	UInt32						asynchCommandArgs[2], saveMaxRT;
4624	OSStatus					status = noErr;
4625
4626	status = FWGetFWIsochResourceManagerID(fwClientID, &fwIsochResourceManagerID);
4627
4628	// JKL *** this is not right since noErr could get returned but not have an IRM
4629	if ((status == noErr) && (fwIsochResourceManagerID != (FWReferenceID) kInvalidFWIsochResourceManagerID))
4630	{
4631		fwCommandObjectID = deviceDescriptionPtr->asynchCommandObjectID;
4632
4633		FWSetFWCommandParams(fwCommandObjectID, fwIsochResourceManagerID, kFWCommandSyncFlag, nil, 0);
4634
4635		// subtract bandwidth with a min at 0
4636		asynchCommandArgs[0] = 0;
4637		asynchCommandArgs[1] = fwBandwidthUnits;
4638		FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000220, (Ptr) &asynchCommandArgs[0], 8);
4639
4640		FWGetAsynchCommandMaxRetries ( fwCommandObjectID, &saveMaxRT );
4641		FWSetAsynchCommandMaxRetries ( fwCommandObjectID, kMaxRetries );
4642
4643		// Subtract from bandwidth allocation.
4644		status = FWThresholdSubtract(fwCommandObjectID);
4645
4646		// Check if bandwidth was allocated.
4647		if (status == noErr)
4648		{
4649			// Bandwidth was allocated if old bandwidth value is greater
4650			// or equal to what we asked for.
4651			if (asynchCommandArgs[0] < asynchCommandArgs[1])
4652				status = insufficientBandwidthErr;
4653		}
4654		FWSetAsynchCommandMaxRetries ( fwCommandObjectID, saveMaxRT );
4655	}
4656
4657	return status;
4658}
4659
4660
4661UInt32 irmCalculateBandwidthUnits(
4662	UInt32						pcrValue)
4663{
4664	UInt32						pcrDataRate,
4665								pcrOverheadID,
4666								pcrPayload,
4667								fwBandwidthUnits;
4668
4669	// set bandwidth units according to 61883 formula and PCR value
4670	// JKL *** all cameras do not set these values correctly, some set the
4671	// overhead too high and don't leave enough bandwidth for two streams
4672
4673	// computer data rate multiplier
4674	pcrDataRate = (pcrValue & 0x0000c000) >> 14;
4675	switch (pcrDataRate)
4676	{
4677		case 0:
4678			pcrDataRate = 16;
4679			break;
4680		case 1:
4681			pcrDataRate = 8;
4682			break;
4683		case 2:
4684			pcrDataRate = 4;
4685			break;
4686	}
4687
4688	pcrPayload = pcrValue & 0x03ff;
4689	pcrOverheadID = (pcrValue & 0x03c00) >> 10;
4690
4691	// 0 is max for overhead
4692	if (!pcrOverheadID)
4693		pcrOverheadID = 16;
4694
4695	fwBandwidthUnits = (pcrOverheadID * 32) + ((pcrPayload + 3) * pcrDataRate);
4696
4697	return fwBandwidthUnits;
4698}
4699
4700
4701OSStatus cmpHandleBusReset(FWClientInterfaceParamsPtr pParams, UInt32 *pCommandAcceptance)
4702{
4703	DeviceDescriptionPtr pDeviceDescription;
4704	OSErr error = noErr;
4705
4706	pDeviceDescription = (DeviceDescriptionPtr) pParams->fwClientSpecificData;
4707
4708	RecordEventLogger( 'cmp ', 'busR', pDeviceDescription->resetStatus, error);
4709	if (pDeviceDescription->resetStatus == kResetInterrupted)
4710		pDeviceDescription->resetStatus = kNoResetsProcessing;
4711
4712	cmpResetStageOne(pDeviceDescription);
4713
4714	FWClientCommandIsComplete(pParams->fwClientCommandID, error);
4715	*pCommandAcceptance = kFWClientCommandAcceptNoMore;
4716
4717	return error;
4718}
4719
4720
4721//
4722// clear local PCR, FSL can do this for us
4723//
4724void cmpResetStageOne(DeviceDescriptionPtr pDeviceDescription)
4725{
4726	OSErr						error = noErr;
4727
4728	RecordEventLogger( 'cmp ', 'res1', pDeviceDescription->resetStatus, error);
4729	if (pDeviceDescription->resetStatus == kNoResetsProcessing)
4730	{
4731		pDeviceDescription->resetStatus = kResetProcessing;
4732
4733		if (pDeviceDescription->inputIsochChannelID)
4734		{
4735			error = cmpResetRead(0xf0000980 + (4 * pDeviceDescription->pcrIndex), pDeviceDescription->localFWReferenceID,
4736								 cmpResetStageTwo, pDeviceDescription);
4737		}
4738		else
4739		{
4740			error = cmpResetRead(0xf0000900 + (4 * pDeviceDescription->pcrIndex), pDeviceDescription->localFWReferenceID,
4741								 cmpResetStageTwo, pDeviceDescription);
4742		}
4743
4744		if (noErr != error)
4745			pDeviceDescription->resetStatus = kNoResetsProcessing;
4746	}
4747	else
4748		pDeviceDescription->resetStatus = kResetInterrupted;
4749}
4750
4751
4752//
4753// complete clearing local PCR
4754//
4755void cmpResetStageTwo(FWCommandObjectID theObjectID, OSStatus status, UInt32 data)
4756{
4757	DeviceDescriptionPtr		pDeviceDescription = (DeviceDescriptionPtr) data;
4758	OSErr						error = noErr;
4759
4760	RecordEventLogger( 'cmp ', 'res2', pDeviceDescription->resetStatus, status);
4761	if (pDeviceDescription->resetStatus == kResetProcessing)
4762	{
4763		if (status == noErr)
4764		{
4765			pDeviceDescription->buffer[0];
4766			if (pDeviceDescription->inputIsochChannelID)
4767			{
4768				pDeviceDescription->buffer[1] = pDeviceDescription->buffer[0] & 0x803f0000;
4769				error = cmpResetWrite(0xf0000980 + (4 * pDeviceDescription->pcrIndex), pDeviceDescription->localFWReferenceID,
4770									 cmpResetStageThree, pDeviceDescription);
4771			}
4772			else
4773			{
4774				pDeviceDescription->buffer[1] = pDeviceDescription->buffer[0] & 0x803fc3ff;
4775				error = cmpResetWrite(0xf0000900 + (4 * pDeviceDescription->pcrIndex), pDeviceDescription->localFWReferenceID,
4776									 cmpResetStageThree, pDeviceDescription);
4777			}
4778		}
4779
4780		if ((noErr != status) || (noErr != error))
4781			pDeviceDescription->resetStatus = kNoResetsProcessing;
4782	}
4783	else
4784	{
4785		// another bus reset came in, start over
4786		pDeviceDescription->resetStatus = kNoResetsProcessing;
4787		cmpResetStageOne(pDeviceDescription);
4788	}
4789}
4790
4791
4792//
4793// re-allocate channel, read IRM channel
4794//
4795void cmpResetStageThree(FWCommandObjectID theObjectID, OSStatus status, UInt32 data)
4796{
4797	DeviceDescriptionPtr		pDeviceDescription = (DeviceDescriptionPtr) data;
4798	FWReferenceID				fwIsochResourceManagerID;
4799	OSErr						error = noErr;
4800
4801	RecordEventLogger( 'cmp ', 'res3', pDeviceDescription->resetStatus, status);
4802	if (pDeviceDescription->resetStatus == kResetProcessing)
4803	{
4804		if (status == noErr)
4805		{
4806			error = FWGetFWIsochResourceManagerID(pDeviceDescription->fwClientID, &fwIsochResourceManagerID);
4807
4808			if (error == noErr)
4809			{
4810				if (pDeviceDescription->cmpChannel < 32)
4811					error = cmpResetRead(0xf0000224, fwIsochResourceManagerID, cmpResetStageFour, pDeviceDescription);
4812				else
4813					error = cmpResetRead(0xf0000228, fwIsochResourceManagerID, cmpResetStageFour, pDeviceDescription);
4814			}
4815		}
4816
4817		if ((noErr != status) || (noErr != error))
4818			pDeviceDescription->resetStatus = kNoResetsProcessing;
4819	}
4820	else
4821	{
4822		// another bus reset came in, start over
4823		pDeviceDescription->resetStatus = kNoResetsProcessing;
4824		cmpResetStageOne(pDeviceDescription);
4825	}
4826}
4827
4828
4829//
4830// complete re-allocating channel
4831// jkl, this might already be allocated
4832void cmpResetStageFour(FWCommandObjectID theObjectID, OSStatus status, UInt32 data)
4833{
4834	DeviceDescriptionPtr		pDeviceDescription = (DeviceDescriptionPtr) data;
4835	FWReferenceID				fwIsochResourceManagerID;
4836	OSErr						error = noErr;
4837
4838	RecordEventLogger( 'cmp ', 'res4', pDeviceDescription->resetStatus, status);
4839	if (pDeviceDescription->resetStatus == kResetProcessing)
4840	{
4841		if (status == noErr)
4842		{
4843			error = FWGetFWIsochResourceManagerID(pDeviceDescription->fwClientID, &fwIsochResourceManagerID);
4844
4845			if (error == noErr)
4846			{
4847				if (pDeviceDescription->cmpChannel < 32)
4848				{
4849					pDeviceDescription->buffer[1] = pDeviceDescription->buffer[0] & ~(0x80000000 >> pDeviceDescription->cmpChannel);
4850					error = cmpResetWrite(0xf0000224, fwIsochResourceManagerID, cmpResetStageFive, pDeviceDescription);
4851				}
4852				else
4853				{
4854					pDeviceDescription->buffer[1] = pDeviceDescription->buffer[0] & ~(0x80000000 >> (pDeviceDescription->cmpChannel - 32));
4855					error = cmpResetWrite(0xf0000228, fwIsochResourceManagerID, cmpResetStageFive, pDeviceDescription);
4856				}
4857			}
4858		}
4859
4860		if ((noErr != status) || (noErr != error))
4861			pDeviceDescription->resetStatus = kNoResetsProcessing;
4862	}
4863	else
4864	{
4865		// another bus reset came in, start over
4866		pDeviceDescription->resetStatus = kNoResetsProcessing;
4867		cmpResetStageOne(pDeviceDescription);
4868	}
4869}
4870
4871
4872//
4873// re-allocate bandwidth
4874// jkl, this might already be allocated
4875void cmpResetStageFive(FWCommandObjectID theObjectID, OSStatus status, UInt32 data)
4876{
4877	FWCommandObjectID			fwCommandObjectID;
4878	DeviceDescriptionPtr		pDeviceDescription = (DeviceDescriptionPtr) data;
4879	FWReferenceID				fwIsochResourceManagerID;
4880	OSErr						error = noErr;
4881	UInt32						saveMaxRT;
4882
4883	RecordEventLogger( 'cmp ', 'res5', pDeviceDescription->resetStatus, status);
4884	if (pDeviceDescription->resetStatus == kResetProcessing)
4885	{
4886		if (status == noErr)
4887		{
4888			error = FWGetFWIsochResourceManagerID(pDeviceDescription->fwClientID, &fwIsochResourceManagerID);
4889
4890			if (error == noErr)
4891			{
4892				fwCommandObjectID = pDeviceDescription->asynchCommandObjectID;
4893
4894				// set command params for synchronous read
4895				error = FWSetFWCommandParams(fwCommandObjectID, fwIsochResourceManagerID, 0, cmpResetStageSix, (UInt32) pDeviceDescription);
4896
4897				// set more command params for synchronous read
4898				if (error == noErr)
4899					error = FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000220, (Ptr) &(pDeviceDescription->buffer[0]), 8);
4900
4901				FWGetAsynchCommandMaxRetries ( fwCommandObjectID, &saveMaxRT );
4902				FWSetAsynchCommandMaxRetries ( fwCommandObjectID, kMaxRetries );
4903
4904				if (error == noErr)
4905				{
4906					pDeviceDescription->buffer[0] = 0;
4907					pDeviceDescription->buffer[1] = pDeviceDescription->cmpBandwidth;
4908
4909					error = FWThresholdSubtract(fwCommandObjectID);
4910				}
4911
4912				FWSetAsynchCommandMaxRetries ( fwCommandObjectID, saveMaxRT );
4913			}
4914		}
4915
4916		if ((noErr != status) || (noErr != error))
4917			pDeviceDescription->resetStatus = kNoResetsProcessing;
4918	}
4919	else
4920	{
4921		// another bus reset came in, start over
4922		pDeviceDescription->resetStatus = kNoResetsProcessing;
4923		cmpResetStageOne(pDeviceDescription);
4924	}
4925}
4926
4927
4928//
4929// set oPCR, read it first
4930//
4931void cmpResetStageSix(FWCommandObjectID theObjectID, OSStatus status, UInt32 data)
4932{
4933	DeviceDescriptionPtr		pDeviceDescription = (DeviceDescriptionPtr) data;
4934	OSErr						error = noErr;
4935
4936	RecordEventLogger( 'cmp ', 'res6', pDeviceDescription->resetStatus, status);
4937	if (pDeviceDescription->resetStatus == kResetProcessing)
4938	{
4939		if (status == noErr)
4940		{
4941			if (pDeviceDescription->inputIsochChannelID)
4942			{
4943				error = cmpResetRead(0xf0000904, pDeviceDescription->fwClientID,
4944									 cmpResetStageSeven, pDeviceDescription);
4945			}
4946			else
4947			{
4948				error = cmpResetRead(0xf0000900 + (4 * pDeviceDescription->pcrIndex), pDeviceDescription->localFWReferenceID,
4949									 cmpResetStageSeven, pDeviceDescription);
4950			}
4951		}
4952
4953		if ((noErr != status) || (noErr != error))
4954			pDeviceDescription->resetStatus = kNoResetsProcessing;
4955
4956	}
4957	else
4958	{
4959		// another bus reset came in, start over
4960		pDeviceDescription->resetStatus = kNoResetsProcessing;
4961		cmpResetStageOne(pDeviceDescription);
4962	}
4963}
4964
4965
4966
4967//
4968// complete oPCR
4969//
4970void cmpResetStageSeven(FWCommandObjectID theObjectID, OSStatus status, UInt32 data)
4971{
4972	DeviceDescriptionPtr		pDeviceDescription = (DeviceDescriptionPtr) data;
4973	OSErr						error = noErr;
4974
4975	RecordEventLogger( 'cmp ', 'res7', pDeviceDescription->resetStatus, status);
4976	if (pDeviceDescription->resetStatus == kResetProcessing)
4977	{
4978		if (status == noErr)
4979		{
4980			if (pDeviceDescription->inputIsochChannelID)
4981			{
4982				pDeviceDescription->buffer[1] = pDeviceDescription->buffer[0] + 0x01000000;
4983				pDeviceDescription->buffer[1] |= (pDeviceDescription->cmpOverheadID << 10);
4984				error = cmpResetWrite(0xf0000904, pDeviceDescription->fwClientID, cmpResetStageEight, pDeviceDescription);
4985			}
4986			else
4987			{
4988				pDeviceDescription->buffer[1] = pDeviceDescription->buffer[0] + 0x01000000;
4989				pDeviceDescription->buffer[1] |= (pDeviceDescription->cmpOverheadID << 10);
4990				error = cmpResetWrite(0xf0000900 + (4 * pDeviceDescription->pcrIndex), pDeviceDescription->localFWReferenceID,
4991									  cmpResetStageEight, pDeviceDescription);
4992			}
4993		}
4994
4995		if ((noErr != status) || (noErr != error))
4996			pDeviceDescription->resetStatus = kNoResetsProcessing;
4997	}
4998	else
4999	{
5000		// another bus reset came in, start over
5001		pDeviceDescription->resetStatus = kNoResetsProcessing;
5002		cmpResetStageOne(pDeviceDescription);
5003	}
5004}
5005
5006
5007//
5008// set iPCR, read it first
5009//
5010void cmpResetStageEight(FWCommandObjectID theObjectID, OSStatus status, UInt32 data)
5011{
5012	DeviceDescriptionPtr		pDeviceDescription = (DeviceDescriptionPtr) data;
5013	OSErr						error = noErr;
5014
5015	RecordEventLogger( 'cmp ', 'res8', pDeviceDescription->resetStatus, status);
5016	if (pDeviceDescription->resetStatus == kResetProcessing)
5017	{
5018		if (status == noErr)
5019		{
5020			if (pDeviceDescription->outputIsochChannelID)
5021			{
5022				error = cmpResetRead(0xf0000984, pDeviceDescription->fwClientID,
5023									 cmpResetStageNine, pDeviceDescription);
5024			}
5025			else
5026			{
5027				error = cmpResetRead(0xf0000980 + (4 * pDeviceDescription->pcrIndex), pDeviceDescription->localFWReferenceID,
5028									 cmpResetStageNine, pDeviceDescription);
5029			}
5030		}
5031
5032		if ((noErr != status) || (noErr != error))
5033			pDeviceDescription->resetStatus = kNoResetsProcessing;
5034	}
5035	else
5036	{
5037		// another bus reset came in, start over
5038		pDeviceDescription->resetStatus = kNoResetsProcessing;
5039		cmpResetStageOne(pDeviceDescription);
5040	}
5041}
5042
5043
5044//
5045// complete iPCR
5046//
5047void cmpResetStageNine(FWCommandObjectID theObjectID, OSStatus status, UInt32 data)
5048{
5049	DeviceDescriptionPtr		pDeviceDescription = (DeviceDescriptionPtr) data;
5050	OSErr						error = noErr;
5051
5052	RecordEventLogger( 'cmp ', 'res9', pDeviceDescription->resetStatus, status);
5053	if (pDeviceDescription->resetStatus == kResetProcessing)
5054	{
5055		if (status == noErr)
5056		{
5057			if (pDeviceDescription->outputIsochChannelID)
5058			{
5059				pDeviceDescription->buffer[1] = (pDeviceDescription->buffer[0] + 0x01000000) & 0xffc0ffff;
5060				pDeviceDescription->buffer[1] |= (pDeviceDescription->cmpChannel << 16);
5061				error = cmpResetWrite(0xf0000984, pDeviceDescription->fwClientID, cmpResetComplete, pDeviceDescription);
5062			}
5063			else
5064			{
5065				pDeviceDescription->buffer[1] = (pDeviceDescription->buffer[0] + 0x01000000) & 0xffc0ffff;
5066				pDeviceDescription->buffer[1] |= (pDeviceDescription->cmpChannel << 16);
5067				error = cmpResetWrite(0xf0000980 + (4 * pDeviceDescription->pcrIndex), pDeviceDescription->localFWReferenceID,
5068									  cmpResetComplete, pDeviceDescription);
5069			}
5070		}
5071
5072		if ((noErr != status) || (noErr != error))
5073			pDeviceDescription->resetStatus = kNoResetsProcessing;
5074
5075	}
5076	else
5077	{
5078		// another bus reset came in, start over
5079		pDeviceDescription->resetStatus = kNoResetsProcessing;
5080		cmpResetStageOne(pDeviceDescription);
5081	}
5082}
5083
5084
5085void cmpResetComplete(FWCommandObjectID theObjectID, OSStatus status, UInt32 data)
5086{
5087	DeviceDescriptionPtr		pDeviceDescription = (DeviceDescriptionPtr) data;
5088	OSErr						error = noErr;
5089
5090	RecordEventLogger( 'cmp ', 'res ', 'comp', status);
5091	pDeviceDescription->resetStatus = kNoResetsProcessing;
5092}
5093
5094
5095//
5096// Do an asynch read
5097//
5098OSErr cmpResetRead (
5099	UInt32						offsetLo,
5100	FWClientID					clientID,
5101	FWCommandCompletionProcPtr	completionProc,
5102	DeviceDescriptionPtr		deviceDescriptionPtr)
5103{
5104	FWCommandObjectID			fwCommandObjectID;
5105	OSErr						error = noErr;
5106	UInt32						saveMaxRT;
5107
5108	fwCommandObjectID = deviceDescriptionPtr->asynchCommandObjectID;
5109
5110	// set command params for synchronous read
5111	error = FWSetFWCommandParams(fwCommandObjectID, clientID, 0, completionProc, (UInt32) deviceDescriptionPtr);
5112
5113	// set more command params for synchronous read
5114	if (error == noErr)
5115		error = FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, offsetLo, (Ptr) &(deviceDescriptionPtr->buffer[0]), 4);
5116
5117	FWGetAsynchCommandMaxRetries ( fwCommandObjectID, &saveMaxRT );
5118	FWSetAsynchCommandMaxRetries ( fwCommandObjectID, kMaxRetries );
5119
5120	if (error == noErr)
5121		error = FWRead(fwCommandObjectID);
5122
5123	FWSetAsynchCommandMaxRetries ( fwCommandObjectID, saveMaxRT );
5124
5125	return error;
5126}
5127
5128
5129//
5130// Do an asynch write, really a lock
5131//
5132OSErr cmpResetWrite (
5133	UInt32						offsetLo,
5134	FWClientID					clientID,
5135	FWCommandCompletionProcPtr	completionProc,
5136	DeviceDescriptionPtr		deviceDescriptionPtr)
5137{
5138	FWCommandObjectID			fwCommandObjectID;
5139	OSErr						error = noErr;
5140	UInt32						saveMaxRT;
5141
5142	fwCommandObjectID = deviceDescriptionPtr->asynchCommandObjectID;
5143
5144	// set command params for synchronous read
5145	error = FWSetFWCommandParams(fwCommandObjectID, clientID, 0, completionProc, (UInt32) deviceDescriptionPtr);
5146
5147	// set more command params for synchronous read
5148	if (error == noErr)
5149		error = FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, offsetLo, (Ptr) &(deviceDescriptionPtr->buffer[0]), 8);
5150
5151	FWGetAsynchCommandMaxRetries ( fwCommandObjectID, &saveMaxRT );
5152	FWSetAsynchCommandMaxRetries ( fwCommandObjectID, kMaxRetries );
5153
5154	if (error == noErr)
5155		error = FWCompareAndSwap(fwCommandObjectID);
5156
5157	FWSetAsynchCommandMaxRetries ( fwCommandObjectID, saveMaxRT );
5158
5159	return error;
5160}
5161
5162
5163Boolean useCMP()
5164{
5165	Handle			h;
5166	Str63			fileName;
5167	CInfoPBRec		catInfoPB;
5168	FSSpec			fileSpec;
5169	SInt32			extfDirID;
5170	SInt16			extfIndex = 1;
5171	SInt16			extfVRefNum, fRef;
5172	SInt32			gestaltVers;
5173	OSErr			err;
5174	Boolean			useCMP = false;	// default is to not use it
5175
5176	// use cmp only if FireWire 2.2 or greater
5177	err = Gestalt(gestaltFireWireVersion, &gestaltVers);
5178	if (err == noErr)
5179	{
5180		if (gestaltVers >= 0x0220)
5181			useCMP = true;
5182	}
5183
5184	// if FireWire 2.2 or greater check if useGestalt resource is available and its value matches
5185	if (useCMP)
5186	{
5187		// get a shortcut to the extensions folder
5188		err = FindFolder(kOnSystemDisk, kExtensionFolderType, kDontCreateFolder, &extfVRefNum, &extfDirID);
5189
5190		if (err == noErr)
5191		{
5192			// iterate through files in extensions folder
5193			while (err == noErr)
5194			{
5195				fileName[ 0 ]					= '\0';
5196				fileName[ 1 ]					= '\0';
5197				catInfoPB.hFileInfo.ioNamePtr	= &fileName[ 0 ];
5198				catInfoPB.hFileInfo.ioVRefNum	= extfVRefNum;
5199				catInfoPB.hFileInfo.ioFDirIndex	= extfIndex++;
5200				catInfoPB.hFileInfo.ioDirID		= extfDirID;
5201				err = PBGetCatInfoSync(&catInfoPB);
5202				if (err == noErr)
5203				{
5204					// find the file matching these attributes
5205					if ((catInfoPB.hFileInfo.ioFlAttrib & 0x10) != 0 ||
5206							(catInfoPB.hFileInfo.ioFlFndrInfo.fdType != 'INIT') ||
5207							(catInfoPB.hFileInfo.ioFlFndrInfo.fdCreator != 'dvfw'))
5208						continue;
5209
5210					// found our file, make an FSSpec
5211					err = FSMakeFSSpec(extfVRefNum, extfDirID, catInfoPB.hFileInfo.ioNamePtr, &fileSpec);
5212
5213					break;
5214
5215				}
5216			}
5217		}
5218
5219		if (err == noErr)
5220		{
5221			fRef = FSpOpenResFile(&fileSpec, fsRdPerm);
5222			err = ResError();
5223			if (err == noErr)
5224			{
5225				h = Get1Resource('ucmp', -20756);
5226				err = ResError();
5227				if ((err == noErr) && (h != nil))
5228					useCMP = true;
5229				else
5230					useCMP = false;
5231
5232				CloseResFile(fRef);
5233			}
5234		}
5235
5236		// turn off useCMP in case of any other error
5237		if (err != noErr)
5238			useCMP = false;
5239	}
5240
5241	return useCMP;
5242}
5243
5244
5245OSErr enableRead(IsochComponentInstancePtr ih)
5246{
5247	FWCommandObjectID			isochChannelCommandObjectID = nil;
5248	DeviceDescriptionPtr		pDeviceDescription;
5249	SInt32						time;
5250	OSErr						error = noErr;
5251
5252	error = findDeviceDescriptionforDevice(ih, ih->deviceID, &pDeviceDescription);
5253	FailMessageVal( error != noErr, error);
5254
5255	if (error == noErr)
5256	{
5257	 	error = dclInitInput(pDeviceDescription, ih->gGlobals);
5258		FailMessageVal( error != noErr, error);
5259	}
5260
5261	if (error == noErr)
5262	{
5263		// JKL, how do we know the speed before doing CMP, IRM
5264		error = FWAllocateIsochronousChannelID(&pDeviceDescription->inputIsochChannelID, false, 30000000, kFWSpeed100MBit);
5265		FailMessageVal( error != noErr, error);
5266 		if (error != noErr)
5267 			disposeDCLInput(pDeviceDescription->pGlobalDVInData);
5268	}
5269
5270	// add a talking client, camera
5271	if (error == noErr)
5272	{
5273		error = FWAddIsochronousChannelClient(pDeviceDescription->inputIsochChannelID, pDeviceDescription->fwClientID, (UInt32) pDeviceDescription, true);
5274		FailMessageVal( error != noErr, error);
5275		if (error != noErr)
5276		{
5277			FWDeallocateIsochronousChannelID(pDeviceDescription->inputIsochChannelID);
5278			pDeviceDescription->inputIsochChannelID = nil;
5279			disposeDCLInput(pDeviceDescription->pGlobalDVInData);
5280		}
5281	}
5282
5283	// add a listening client, mac
5284	if (error == noErr)
5285	{
5286		error = FWAddIsochronousChannelClient(pDeviceDescription->inputIsochChannelID, pDeviceDescription->fwClientID, (UInt32) pDeviceDescription, false);
5287		FailMessageVal( error != noErr, error);
5288		if( error != noErr )
5289		{
5290			FWDeallocateIsochronousChannelID(pDeviceDescription->inputIsochChannelID);
5291			pDeviceDescription->inputIsochChannelID = nil;
5292 			disposeDCLInput(pDeviceDescription->pGlobalDVInData);
5293		}
5294	}
5295
5296	// Initialize the isochronous channel
5297	if (error == noErr)
5298	{
5299		if (pDeviceDescription->componentGlobals->useCMP)
5300		{
5301			pDeviceDescription->resetStatus = kNoResetsProcessing;
5302
5303			// allocate PCR space from FSL
5304			// JKL, for now should get pcr #1, what if we don't
5305			error = FWAllocatePCRAddressSpace(&pDeviceDescription->pcrID, pDeviceDescription->fwClientID,
5306											  (Ptr) &pDeviceDescription->pcrBuffer,
5307											  kFWAddressReadEnable | kFWAddressLockEnable |
5308											  kFWAddressLockRequestNotify | kFWAddressLockCompleteNotify,
5309											  (Ptr) pDeviceDescription, true /* inputPCR */, 1, &pDeviceDescription->pcrIndex);
5310			FailMessageVal( error != noErr, error);
5311
5312	// JKL, not using lock call backs, could be used to respond to device trying to modify local PCR's
5313	//		if (error == noErr)
5314	//			error = FWSetFWClientLockCompleteProc(pDeviceDescription->fwClientID, pcrHandleLocalLock);
5315	//
5316	//		if (error == noErr)
5317	//			error = FWSetFWClientLockRequestProc(pDeviceDescription->fwClientID, pcrHandleLocalLock);
5318
5319			// add bus reset proc
5320	 		if (error == noErr)
5321	 			error = FWSetFWClientBusManagementNotifyProc(pDeviceDescription->fwClientID, (FWClientResetNotifyProcPtr) cmpHandleBusReset);
5322	 		FailMessageVal( error != noErr, error);
5323		}
5324
5325		// register for bus reset notification from FireWire for DV deviceChanged notification
5326		// unregister at release
5327	 	if (error == noErr)
5328		{
5329			error = FWSetFWClientResetNotifyProc(pDeviceDescription->fwClientID, handleBusReset);
5330	 		FailMessageVal( error != noErr, error);
5331		}
5332
5333		if (error == noErr)
5334		{
5335			error = FWAllocateIsochChannelCommandObject(&isochChannelCommandObjectID);
5336			FailMessageVal( error != noErr, error);
5337		}
5338
5339		if (error == noErr)
5340		{
5341			FWSetFWCommandParams(isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, kFWCommandSyncFlag, nil, 0);
5342			FWSetIsochChannelCommandIsochChannelID(isochChannelCommandObjectID, pDeviceDescription->inputIsochChannelID);
5343			error = FWInitializeIsochronousChannel(isochChannelCommandObjectID);
5344			FailMessageVal( error != noErr, error);
5345		}
5346
5347		if	(error != noErr)
5348		{
5349			// release channel even though init failed, part of init (listener or talker) may have succeeded
5350			FWReleaseIsochronousChannel(isochChannelCommandObjectID);
5351			FWDeallocateIsochronousChannelID(pDeviceDescription->inputIsochChannelID);
5352			pDeviceDescription->inputIsochChannelID = nil;
5353			disposeDCLInput(pDeviceDescription->pGlobalDVInData);
5354		}
5355		else
5356		{
5357			// Start the isochronous channel
5358			error = FWSetFWCommandParams(isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, kFWCommandSyncFlag, nil, 0);
5359			FailMessageVal( error != noErr, error);
5360			error = FWSetIsochChannelCommandIsochChannelID(isochChannelCommandObjectID, pDeviceDescription->inputIsochChannelID);
5361			FailMessageVal( error != noErr, error);
5362
5363			// JKL, revisit this.
5364			// delay because some (many) cameras send bad data immediately after you change the plug control registers
5365			// (at least that's my guess, It could be a bug somewhere in FSL... who knows...
5366
5367			// we reprogram the PCRs with the above FWInitializeIsochronousChannel call
5368
5369			// the delay was arrived at by good ole' trial and error
5370			time = TickCount();
5371			while( TickCount() < (time + 7) )
5372			{
5373				// Yes...really do nothing
5374				;
5375			}
5376
5377			error = FWStartIsochronousChannel(isochChannelCommandObjectID);
5378			FailMessageVal( error != noErr, error);
5379			if (error != noErr)
5380			{
5381				FWReleaseIsochronousChannel(isochChannelCommandObjectID);
5382				FWDeallocateIsochronousChannelID(pDeviceDescription->inputIsochChannelID);
5383				pDeviceDescription->inputIsochChannelID = nil;
5384				disposeDCLInput(pDeviceDescription->pGlobalDVInData);
5385			}
5386		}
5387	}
5388
5389	if (isochChannelCommandObjectID)
5390		FWDeallocateFWCommandObject(isochChannelCommandObjectID);
5391
5392	// JKL, readEnabledNotification?
5393	if (error)
5394		FWSetFWClientResetNotifyProc(pDeviceDescription->fwClientID, nil);
5395
5396	FailMessageVal( error != noErr, error);
5397
5398	return error;
5399}
5400
5401
5402OSErr disableRead(IsochComponentInstancePtr ih)
5403{
5404	FWCommandObjectID			isochChannelCommandObjectID = nil;
5405	DeviceDescriptionPtr		pDeviceDescription;
5406	OSErr						error = noErr;
5407
5408	error = findDeviceDescriptionforDevice(ih, ih->deviceID, &pDeviceDescription);
5409
5410	if (error == noErr)
5411	{
5412		if (pDeviceDescription->inputIsochChannelID)
5413		{
5414			if (error == noErr)
5415				error = FWAllocateIsochChannelCommandObject(&isochChannelCommandObjectID);
5416
5417			if (error == noErr)
5418			{
5419				// stop channel
5420				FWSetFWCommandParams(isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, kFWCommandSyncFlag, nil, 0);
5421				FWSetIsochChannelCommandIsochChannelID(isochChannelCommandObjectID, pDeviceDescription->inputIsochChannelID);
5422				FWStopIsochronousChannel(isochChannelCommandObjectID);
5423
5424				// release channel
5425				FWSetFWCommandParams(isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, kFWCommandSyncFlag, nil, 0);
5426				FWSetIsochChannelCommandIsochChannelID(isochChannelCommandObjectID, pDeviceDescription->inputIsochChannelID);
5427				FWReleaseIsochronousChannel(isochChannelCommandObjectID);
5428				FWDeallocateIsochronousChannelID(pDeviceDescription->inputIsochChannelID);
5429				pDeviceDescription->inputIsochChannelID = 0;
5430
5431				if (pDeviceDescription->componentGlobals->useCMP)
5432				{
5433					// JKL, should clear pcr before deallocating?
5434					FWDeallocateAddressSpace(pDeviceDescription->pcrID);
5435					pDeviceDescription->pcrID = 0;
5436					pDeviceDescription->pcrIndex = 0;
5437
5438			 		FWSetFWClientBusManagementNotifyProc(pDeviceDescription->fwClientID, (FWClientResetNotifyProcPtr) nil);
5439
5440			// JKL, not using lock call backs or bus management yet
5441			//		FWSetFWClientLockCompleteProc(pDeviceDescription->fwClientID, (FWClientLockProcPtr) nil);
5442			//		FWSetFWClientLockRequestProc(pDeviceDescription->fwClientID, (FWClientLockProcPtr) nil);
5443				}
5444			}
5445
5446			// unregister bus reset notification
5447			FWSetFWClientResetNotifyProc(pDeviceDescription->fwClientID, (FWClientResetNotifyProcPtr) nil);
5448
5449			disposeDCLInput(pDeviceDescription->pGlobalDVInData);
5450		}
5451	}
5452	return error;
5453}
5454
5455
5456OSErr enableWrite(IsochComponentInstancePtr ih)
5457{
5458	FWCommandObjectID			isochChannelCommandObjectID = nil;
5459	DeviceDescriptionPtr		pDeviceDescription;
5460	SInt32						time;
5461	OSErr						error = noErr;
5462
5463	error = findDeviceDescriptionforDevice(ih, ih->deviceID, &pDeviceDescription);
5464
5465	if (error == noErr)
5466 		error = dclInitOutput(pDeviceDescription, ih->gGlobals);
5467
5468	if (error == noErr)
5469	{
5470		// JKL, how do we know the speed before doing CMP, IRM
5471		error = FWAllocateIsochronousChannelID(&pDeviceDescription->outputIsochChannelID, false, 30000000, kFWSpeed100MBit);
5472 		if (error != noErr)
5473 			disposeDCLOutput(pDeviceDescription->pGlobalDVOutData);
5474	}
5475
5476	// add a talking client, mac
5477	if (error == noErr)
5478	{
5479		error = FWAddIsochronousChannelClient(pDeviceDescription->outputIsochChannelID, pDeviceDescription->fwClientID, (UInt32) pDeviceDescription, true);
5480		if (error != noErr)
5481		{
5482			FWDeallocateIsochronousChannelID(pDeviceDescription->outputIsochChannelID);
5483			pDeviceDescription->outputIsochChannelID = nil;
5484			disposeDCLOutput(pDeviceDescription->pGlobalDVOutData);
5485		}
5486	}
5487
5488	// add a listening client, camera
5489	if (error == noErr)
5490	{
5491		error = FWAddIsochronousChannelClient(pDeviceDescription->outputIsochChannelID, pDeviceDescription->fwClientID, (UInt32) pDeviceDescription, false);
5492		if (error != noErr)
5493		{
5494			FWDeallocateIsochronousChannelID(pDeviceDescription->outputIsochChannelID);
5495			pDeviceDescription->outputIsochChannelID = nil;
5496			disposeDCLOutput(pDeviceDescription->pGlobalDVOutData);
5497		}
5498	}
5499
5500	// Initialize the isochronous channel
5501	if (error == noErr)
5502	{
5503
5504		if (pDeviceDescription->componentGlobals->useCMP)
5505		{
5506			pDeviceDescription->resetStatus = kNoResetsProcessing;
5507
5508			// allocate PCR space from FSL
5509			// JKL, for now should get pcr #1, what if we don't
5510			error = FWAllocatePCRAddressSpace(&pDeviceDescription->pcrID, pDeviceDescription->fwClientID,
5511											  (Ptr) &pDeviceDescription->pcrBuffer,
5512											  kFWAddressReadEnable | kFWAddressLockEnable |
5513											  kFWAddressLockRequestNotify | kFWAddressLockCompleteNotify,
5514											  (Ptr) pDeviceDescription, false /* outputPCR */, 1, &pDeviceDescription->pcrIndex);
5515			FailMessageVal( error != noErr, error);
5516
5517	// JKL, not using lock call backs, could be used to respond to a device trying to modify local PCR's
5518	//		if (error == noErr)
5519	//			error = FWSetFWClientLockCompleteProc(pDeviceDescription->fwClientID, cmpHandleLocalLock);
5520	//
5521	//		if (error == noErr)
5522	//			error = FWSetFWClientLockRequestProc(pDeviceDescription->fwClientID, cmpHandleLocalLock);
5523
5524			// add bus reset proc
5525	 		if (error == noErr)
5526	 			error = FWSetFWClientBusManagementNotifyProc(pDeviceDescription->fwClientID, (FWClientResetNotifyProcPtr) cmpHandleBusReset);
5527	 		FailMessageVal( error != noErr, error);
5528		}
5529
5530		// register for bus reset notification from FireWire for DV deviceChanged notification
5531		// unregister at release
5532	 	if (error == noErr)
5533		{
5534			error = FWSetFWClientResetNotifyProc(pDeviceDescription->fwClientID, handleBusReset);
5535	 		FailMessageVal( error != noErr, error);
5536		}
5537
5538		if (error == noErr)
5539		{
5540			error = FWAllocateIsochChannelCommandObject(&isochChannelCommandObjectID);
5541			FailMessageVal( error != noErr, error);
5542		}
5543
5544		if (error == noErr)
5545		{
5546			FWSetFWCommandParams(isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, kFWCommandSyncFlag, nil, 0);
5547			FWSetIsochChannelCommandIsochChannelID(isochChannelCommandObjectID, pDeviceDescription->outputIsochChannelID);
5548			error = FWInitializeIsochronousChannel(isochChannelCommandObjectID);
5549		}
5550
5551		if (error != noErr)
5552		{
5553			// release channel even though init failed, part of init (listener or talker) may have succeeded
5554			FWReleaseIsochronousChannel(isochChannelCommandObjectID);
5555			FWDeallocateIsochronousChannelID(pDeviceDescription->outputIsochChannelID);
5556			pDeviceDescription->outputIsochChannelID = nil;
5557			disposeDCLOutput(pDeviceDescription->pGlobalDVOutData);
5558		}
5559		else
5560		{
5561			// Start the isochronous channel
5562			error = FWSetFWCommandParams(isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, kFWCommandSyncFlag, nil, 0);
5563			error = FWSetIsochChannelCommandIsochChannelID(isochChannelCommandObjectID, pDeviceDescription->outputIsochChannelID);
5564
5565			// delay because some (many) cameras send bad data immediately after you change the plug control registers
5566			// (at least that's my guess, It could be a bug somewhere in FSL... who knows...
5567			// we reprogram the PCRs with the above FWInitializeIsochronousChannel call
5568			// the delay was arrived at by good ole' trial and error
5569
5570			error = FWStartIsochronousChannel(isochChannelCommandObjectID);
5571			if (error == noErr)
5572			{
5573				time = TickCount();
5574				while( TickCount() < (time + 60) )
5575				{
5576					// Yes...really do nothing
5577					;
5578				}
5579			}
5580			else
5581			{
5582				FWReleaseIsochronousChannel(isochChannelCommandObjectID);
5583				FWDeallocateIsochronousChannelID(pDeviceDescription->outputIsochChannelID);
5584				pDeviceDescription->outputIsochChannelID = nil;
5585				disposeDCLOutput(pDeviceDescription->pGlobalDVOutData);
5586			}
5587		}
5588	}
5589
5590	if (error)
5591		FWSetFWClientResetNotifyProc(pDeviceDescription->fwClientID, nil);
5592
5593	if (isochChannelCommandObjectID)
5594		FWDeallocateFWCommandObject(isochChannelCommandObjectID);
5595
5596	// JKL, writeEnabledNotification
5597
5598	return error;
5599}
5600
5601
5602OSErr disableWrite(IsochComponentInstancePtr ih)
5603{
5604	FWCommandObjectID			isochChannelCommandObjectID = nil;
5605	DeviceDescriptionPtr		pDeviceDescription;
5606	OSErr						error = noErr;
5607
5608	error = findDeviceDescriptionforDevice(ih, ih->deviceID, &pDeviceDescription);
5609
5610	if (error == noErr)
5611	{
5612		if (pDeviceDescription->outputIsochChannelID)
5613		{
5614			if (error == noErr)
5615				error = FWAllocateIsochChannelCommandObject(&isochChannelCommandObjectID);
5616
5617			if (error == noErr)
5618			{
5619				// JKL, need top delay stopping transmit until all queued frames are sent
5620				// originally had a isFinished call and then delayed 2 * ticks * howeverManyFramesAreQueued
5621				// stop channel
5622				FWSetFWCommandParams(isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, kFWCommandSyncFlag, nil, 0);
5623				FWSetIsochChannelCommandIsochChannelID(isochChannelCommandObjectID, pDeviceDescription->outputIsochChannelID);
5624				FWStopIsochronousChannel(isochChannelCommandObjectID);
5625
5626				// release channel
5627				FWSetFWCommandParams(isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, kFWCommandSyncFlag, nil, 0);
5628				FWSetIsochChannelCommandIsochChannelID(isochChannelCommandObjectID, pDeviceDescription->outputIsochChannelID);
5629				FWReleaseIsochronousChannel(isochChannelCommandObjectID);
5630				FWDeallocateIsochronousChannelID(pDeviceDescription->outputIsochChannelID);
5631				pDeviceDescription->outputIsochChannelID = 0;
5632
5633				if (pDeviceDescription->componentGlobals->useCMP)
5634				{
5635					// jkl, should clear pcr before deallocating
5636					FWDeallocateAddressSpace(pDeviceDescription->pcrID);
5637					pDeviceDescription->pcrID = 0;
5638					pDeviceDescription->pcrIndex = 0;
5639
5640					FWSetFWClientBusManagementNotifyProc(pDeviceDescription->fwClientID, (FWClientResetNotifyProcPtr) nil);
5641
5642			// JKL, not using lock call backs or bus management yet
5643			//		FWSetFWClientLockCompleteProc(pDeviceDescription->fwClientID, (FWClientLockProcPtr) nil);
5644			//		FWSetFWClientLockRequestProc(pDeviceDescription->fwClientID, (FWClientLockProcPtr) nil);
5645				}
5646			}
5647
5648			// unregister bus reset notification
5649			FWSetFWClientResetNotifyProc(pDeviceDescription->fwClientID, (FWClientResetNotifyProcPtr) nil);
5650
5651			disposeDCLOutput(pDeviceDescription->pGlobalDVOutData);
5652		}
5653	}
5654	return error;
5655}
5656
5657
5658OSErr dclInitOutput(DeviceDescriptionPtr pDeviceDescription,
5659		IsochComponentGlobals* isochComponentGlobals)
5660{
5661	float						A, B, C, d, n;
5662	UInt32						numEmptyPacketsPerPlayBufferGroup;
5663	UInt32						transmitBuffersSize;
5664	DCLCommandPoolPtr			pDCLCommandPool;
5665	DCLCommandPtr				pDCLCommand;
5666	DCLCommandPtr				pFirstBufferGroupDCLCommand;
5667	DVGlobalOutPtr				pGlobalData;
5668	DCLLabelPtr					pUnderrunDCLLabel,
5669								pLoopDCLLabel,
5670								pBufferGroupDCLLabel,
5671								pDCLLabel;
5672	DCLTransferPacketPtr		pDCLTransferPacket;
5673	DCLCallProcPtr				pDCLCallProc;
5674	DCLSetTagSyncBitsPtr		pDCLSetTagSyncBits;
5675	DCLJumpPtr					pDCLJump,
5676								pBufferGroupDCLJump;
5677	DCLLabelPtr					pBufferGroupSkipEmptyPacketDCLLabel;
5678	DCLUpdateDCLListPtr			pDCLUpdateDCLList;
5679	DCLTimeStampPtr				pDCLTimeStamp;
5680
5681	DVLocalOutPtr				pPlayBufferGroupData;
5682	UInt32						*pTransmitBuffer;
5683	UInt32						bufferGroupNum,
5684								dataPacketNum,
5685								numPackets;
5686	UInt32						emptyPacketNumerator;
5687	UInt32						playFrameRateNumerator, playFrameRateDenominator;
5688	UInt32						numDataPacketsPerPage;
5689	UInt32						pageSize;
5690	OSErr						error = noErr;
5691
5692	// allocate space for global data
5693	pGlobalData = PoolAllocateResident(sizeof(DVGlobalOut), true);
5694	if (pGlobalData == nil)
5695		error = memFullErr;
5696
5697	if (error == noErr)
5698	{
5699		pDeviceDescription->pGlobalDVOutData = pGlobalData;
5700
5701		pGlobalData->isochComponentGlobals = isochComponentGlobals;
5702		pGlobalData->deviceID = pDeviceDescription->deviceID;
5703		pGlobalData->pBufferGroupDataList = nil;
5704		pGlobalData->pDCLCommandPool = nil;
5705		pGlobalData->pDVFrameOutputData = nil;
5706		pGlobalData->pTransmitBuffers = nil;
5707		pGlobalData->isNTSC = (pDeviceDescription->standard == ntscIn);
5708		pGlobalData->localFWReferenceID = pDeviceDescription->localFWReferenceID;
5709
5710		error = initDVFrameOutput(pDeviceDescription->standard, &pGlobalData->pDVFrameOutputData);
5711	}
5712
5713	if (error == noErr)
5714	{
5715		error = FWCreateDCLProgram(&pGlobalData->DCLProgramID);
5716		pDeviceDescription->dclProgramID = pGlobalData->DCLProgramID;
5717	}
5718
5719	if (error == noErr)
5720	{
5721		error = FWAllocateIsochChannelCommandObject(&pGlobalData->isochChannelCommandObjectID);
5722	}
5723
5724
5725	if (error == noErr)
5726	{
5727		if( pGlobalData->isNTSC )
5728		{
5729			pGlobalData->playFramePeriodNumerator = kNTSCPlayFramePeriodNumerator;
5730			pGlobalData->playFramePeriodDenominator = kNTSCPlayFramePeriodDenominator;
5731			playFrameRateNumerator = kNTSCFrameRateNumerator;
5732			playFrameRateDenominator = kNTSCFrameRateDenominator;
5733			pGlobalData->numDataPacketsPerFrame = kNTSCNumDataPacketsPerDVFrame;
5734		}
5735		else
5736		{
5737			pGlobalData->playFramePeriodNumerator = kPALPlayFramePeriodNumerator;
5738			pGlobalData->playFramePeriodDenominator = kPALPlayFramePeriodDenominator;
5739			playFrameRateNumerator = kPALFrameRateNumerator;
5740			playFrameRateDenominator = kPALFrameRateDenominator;
5741			pGlobalData->numDataPacketsPerFrame = kPALNumDataPacketsPerDVFrame;
5742		}
5743
5744		// Compute nominal frame period cycle time.
5745		pGlobalData->nominalFrameCycleTime = convertFractionalSecondsToFWCycleTime
5746														(pGlobalData->playFramePeriodNumerator, pGlobalData->playFramePeriodDenominator);
5747
5748		// Compute the number of data packets per empty packet.
5749		// If the frame rate is expressed as n/d, the number of data packets per buffer group
5750		// expressed as A, and the number of data packets per frame as C, then the number of
5751		// empty packets per buffer group B should be
5752		//
5753		// B = int (8000*d/n*A/C - A + 1)
5754		//
5755		// in order to ensure that the frame rate may be maintained by periodically reducing
5756		// the number of empty packets in a buffer group by 1.
5757		//
5758		A = (float) kNumDataPacketsPerPlayBufferGroup;
5759		C = (float) pGlobalData->numDataPacketsPerFrame;
5760		n = (float) playFrameRateNumerator;
5761		d = (float) playFrameRateDenominator;
5762		B = 8000.0*d/n*A/C - A + 1;
5763		numEmptyPacketsPerPlayBufferGroup = (UInt32) B;
5764
5765
5766		/////////////////////////////////////////
5767		// Allocate transmit buffers.
5768		//
5769
5770		// Compute size.
5771
5772		// to prevent packets crossing page boundaries
5773		// allocate buffer memory based on pages,
5774		// there is enough extra room in each page for the empty packets
5775		pageSize = GetLogicalPageSize();
5776		numDataPacketsPerPage = pageSize / (kDVPacketTransferSize + kDVPacketAlignSlop);
5777		transmitBuffersSize = kNumDataPacketsPerPlayBufferGroup / numDataPacketsPerPage * pageSize;
5778		transmitBuffersSize *= kNumPlayBufferGroups;
5779
5780		// add an extra page to account for first page not being page aligned
5781		transmitBuffersSize += pageSize;
5782
5783		// Allocate.
5784		pGlobalData->pTransmitBuffers = PoolAllocateResident(transmitBuffersSize, false);
5785		if (pGlobalData->pTransmitBuffers == nil)
5786			error = memFullErr;
5787		else
5788			pTransmitBuffer = (UInt32 *) pGlobalData->pTransmitBuffers;
5789	}
5790
5791	if (error == noErr)
5792	{
5793
5794		/////////////////////////////////////////
5795		// Start Up DCL Allocation Engine
5796		// Allocate DCL command pool.
5797
5798		pDCLCommandPool = dclAllocateCommandPool();
5799		if (pDCLCommandPool == nil)
5800			error = memFullErr;
5801		else
5802			pGlobalData->pDCLCommandPool = pDCLCommandPool;
5803	}
5804
5805	if (error == noErr)
5806	{
5807		////////////////////////////////////
5808		// Create DCL Program
5809		//
5810
5811		// Initialize total packet count.
5812		pGlobalData->totalPackets = 0;
5813
5814		// Create label for start of loop.
5815		pLoopDCLLabel = (DCLLabelPtr) dclAllocateCommand(pDCLCommandPool, sizeof (DCLLabel));
5816		//zzz check error
5817		pDCLCommand = (DCLCommandPtr) pLoopDCLLabel;
5818
5819		pGlobalData->pDCLList = pDCLCommand;
5820		pLoopDCLLabel->opcode = kDCLLabelOp;
5821
5822		// Set isoch packet tag bits to the way DV likes 'em
5823		pDCLSetTagSyncBits = (DCLSetTagSyncBitsPtr) dclAllocateCommand(pDCLCommandPool, sizeof (DCLSetTagSyncBits));
5824		//zzz check error
5825		pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLSetTagSyncBits;
5826		pDCLCommand = (DCLCommandPtr) pDCLSetTagSyncBits;
5827		pDCLSetTagSyncBits->opcode = kDCLSetTagSyncBitsOp;
5828		pDCLSetTagSyncBits->tagBits = 1;
5829		pDCLSetTagSyncBits->syncBits = 0;
5830
5831		for (bufferGroupNum = 0; bufferGroupNum < kNumPlayBufferGroups; bufferGroupNum++)
5832		{
5833			// Allocate a buffer group data record.
5834			pPlayBufferGroupData = dclAllocatePlayBufferGroup(pGlobalData);
5835			if (pPlayBufferGroupData == nil)
5836			{
5837				error = memFullErr;
5838				goto bail;
5839			}
5840
5841
5842			// Initialize for loop.
5843			dataPacketNum = 0;
5844			numPackets = 0;
5845			emptyPacketNumerator = 0;
5846			pFirstBufferGroupDCLCommand = nil;
5847			pBufferGroupSkipEmptyPacketDCLLabel = nil;
5848
5849			while (dataPacketNum < kNumDataPacketsPerPlayBufferGroup)
5850			{
5851				// Send a packet: CIP header + payload.
5852				pDCLTransferPacket = (DCLTransferPacketPtr) dclAllocateCommand(pDCLCommandPool, sizeof (DCLTransferPacket));
5853				if( pDCLTransferPacket == nil )
5854				{
5855					error = memFullErr;
5856					goto bail;
5857				}
5858				pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLTransferPacket;
5859				pDCLCommand = (DCLCommandPtr) pDCLTransferPacket;
5860				pDCLTransferPacket->opcode = kDCLSendPacketStartOp;
5861				pDCLTransferPacket->size = kDVPacketTransferSize;
5862
5863				// check for buffer crossing page
5864				if (((UInt32) (pTransmitBuffer + kDVPacketAlignOffset) & 0x0fff) < (kDVPacketAlignOffset * 4))
5865				{
5866					// if it does, increment buffer pointer
5867					// and lop off page rollover to start at next page
5868					pTransmitBuffer += kDVPacketAlignOffset;
5869					pTransmitBuffer = (UInt32 *) ((UInt32) pTransmitBuffer & 0xfffff000);
5870				}
5871
5872				pDCLTransferPacket->buffer = (Ptr) pTransmitBuffer;
5873				// increment by 496 bytes to maintain cache alignment
5874				pTransmitBuffer += kDVPacketAlignOffset;
5875
5876				// Save first data packet DCL command.
5877				if (pFirstBufferGroupDCLCommand == nil)
5878					pFirstBufferGroupDCLCommand = (DCLCommandPtr) pDCLCommand;
5879
5880				dataPacketNum++;
5881				numPackets++;
5882				emptyPacketNumerator += numEmptyPacketsPerPlayBufferGroup;
5883
5884				if (emptyPacketNumerator >= kNumDataPacketsPerPlayBufferGroup)
5885				{
5886					// Add skip jump if this is the first empty packet in the buffer group.
5887					if (pBufferGroupSkipEmptyPacketDCLLabel == nil)
5888					{
5889						pDCLJump = (DCLJumpPtr) dclAllocateCommand(pDCLCommandPool, sizeof (DCLJump));
5890						if( pDCLJump == nil )
5891						{
5892							error = memFullErr;
5893							goto bail;
5894						}
5895						pPlayBufferGroupData->pBufferGroupSkipEmptyPacketDCLJump = pDCLJump;
5896						pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLJump;
5897						pDCLCommand = (DCLCommandPtr) pDCLJump;
5898						pDCLJump->opcode = kDCLJumpOp | kFWDCLOpDynamicFlag;
5899
5900						pDCLLabel = (DCLLabelPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLLabel));
5901						if( pDCLLabel == nil )
5902						{
5903							error = memFullErr;
5904							goto bail;
5905						}
5906						pPlayBufferGroupData->pBufferGroupDontSkipEmptyPacketDCLLabel = pDCLLabel;
5907						pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLLabel;
5908						pDCLCommand = (DCLCommandPtr) pDCLLabel;
5909						pDCLLabel->opcode = kDCLLabelOp;
5910
5911						pDCLJump->pJumpDCLLabel = pDCLLabel;
5912					}
5913
5914					// Send a packet.
5915					// Just CIP header.
5916					pDCLTransferPacket = (DCLTransferPacketPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLTransferPacket));
5917					if( pDCLTransferPacket == nil )
5918					{
5919						error = memFullErr;
5920						goto bail;
5921					}
5922					pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLTransferPacket;
5923					pDCLCommand = (DCLCommandPtr) pDCLTransferPacket;
5924					pDCLTransferPacket->opcode = kDCLSendPacketStartOp;
5925					pDCLTransferPacket->buffer = (Ptr) pTransmitBuffer;
5926					pDCLTransferPacket->size = kDVPacketCIPSize;
5927
5928					// increment 16 bytes to maintain alignment
5929					pTransmitBuffer += kDVEmptyPacketAlignOffset;
5930					numPackets++;
5931					emptyPacketNumerator -= kNumDataPacketsPerPlayBufferGroup;
5932
5933					// Add skip jump label if this is the first empty packet in the
5934					// buffer group.
5935					if (pBufferGroupSkipEmptyPacketDCLLabel == nil)
5936					{
5937						// Add skip label.
5938						pDCLLabel = (DCLLabelPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLLabel));
5939						if( pDCLLabel == nil )
5940						{
5941							error = memFullErr;
5942							goto bail;
5943						}
5944						pBufferGroupSkipEmptyPacketDCLLabel = pDCLLabel;
5945						pPlayBufferGroupData->pBufferGroupSkipEmptyPacketDCLLabel = pBufferGroupSkipEmptyPacketDCLLabel;
5946						pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLLabel;
5947						pDCLCommand = (DCLCommandPtr) pDCLLabel;
5948						pDCLLabel->opcode = kDCLLabelOp;
5949					}
5950				}
5951			}
5952
5953			// Save number of packets in this buffer group, DCL update list size, and last
5954			// DCL command.
5955			pPlayBufferGroupData->numPackets = numPackets;
5956			pPlayBufferGroupData->pFirstBufferGroupDCLCommand = pFirstBufferGroupDCLCommand;
5957			pPlayBufferGroupData->pLastBufferGroupDCLCommand = (DCLCommandPtr) pDCLCommand;
5958
5959			// Create buffer group update list.
5960			createDVPlayBufferGroupUpdateList( pPlayBufferGroupData );
5961
5962			// Update total packet count.
5963			pGlobalData->totalPackets += numPackets;
5964
5965			// Create end of buffer group jump.
5966			pBufferGroupDCLJump = (DCLJumpPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLJump));
5967			if( pBufferGroupDCLJump == nil )
5968			{
5969				error = memFullErr;
5970				goto bail;
5971			}
5972			pPlayBufferGroupData->pEndOfBufferGroupDCLJump = pBufferGroupDCLJump;
5973			pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pBufferGroupDCLJump;
5974			pDCLCommand = (DCLCommandPtr) pBufferGroupDCLJump;
5975			pBufferGroupDCLJump->opcode = kDCLJumpOp | kFWDCLOpDynamicFlag;
5976
5977			// Create label for end of buffer group.
5978			pBufferGroupDCLLabel = (DCLLabelPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLLabel));
5979			if( pBufferGroupDCLLabel == nil )
5980			{
5981				error = memFullErr;
5982				goto bail;
5983			}
5984			pPlayBufferGroupData->pEndOfBufferGroupDCLLabel = pBufferGroupDCLLabel;
5985			pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pBufferGroupDCLLabel;
5986			pDCLCommand = (DCLCommandPtr) pBufferGroupDCLLabel;
5987			pBufferGroupDCLLabel->opcode = kDCLLabelOp;
5988
5989			// Set end of buffer group jump to jump to end of buffer group.
5990			pBufferGroupDCLJump->pJumpDCLLabel = pBufferGroupDCLLabel;
5991
5992			// Get time stamp at end of buffer group.
5993			pDCLTimeStamp = (DCLTimeStampPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLTimeStamp));
5994			if( pDCLTimeStamp == nil )
5995			{
5996				error = memFullErr;
5997				goto bail;
5998			}
5999			pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLTimeStamp;
6000			pDCLCommand = (DCLCommandPtr) pDCLTimeStamp;
6001			pDCLTimeStamp->opcode = kDCLTimeStampOp;
6002			pPlayBufferGroupData->pBufferGroupDCLTimeStamp = pDCLTimeStamp;
6003			pPlayBufferGroupData->timeStampUpdateDCLList = (DCLCommandPtr) pDCLTimeStamp;
6004
6005			// Create update DCL list to update time stamp.
6006			pDCLUpdateDCLList = (DCLUpdateDCLListPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLUpdateDCLList));
6007			if( pDCLUpdateDCLList == nil )
6008			{
6009				error = memFullErr;
6010				goto bail;
6011			}
6012			pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLUpdateDCLList;
6013			pDCLCommand = (DCLCommandPtr) pDCLUpdateDCLList;
6014			pDCLUpdateDCLList->opcode = kDCLUpdateDCLListOp;
6015			pDCLUpdateDCLList->dclCommandList = &(pPlayBufferGroupData->timeStampUpdateDCLList);
6016			pDCLUpdateDCLList->numDCLCommands = 1;
6017
6018			// Call a proc at end of buffer group.
6019			pDCLCallProc = (DCLCallProcPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLCallProc));
6020			if( pDCLCallProc == nil )
6021			{
6022				error = memFullErr;
6023				goto bail;
6024			}
6025			pPlayBufferGroupData->pBufferGroupDCLCallProc = pDCLCallProc;
6026			pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLCallProc;
6027			pDCLCommand = (DCLCommandPtr) pDCLCallProc;
6028			pDCLCallProc->opcode = kDCLCallProcOp;
6029			pDCLCallProc->proc = handleDVOutput;
6030			pDCLCallProc->procData = (UInt32) pPlayBufferGroupData;
6031
6032			// Create update DCL list to update buffers.
6033			pDCLUpdateDCLList = (DCLUpdateDCLListPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLUpdateDCLList));
6034			if( pDCLUpdateDCLList == nil )
6035			{
6036				error = memFullErr;
6037				goto bail;
6038			}
6039			pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLUpdateDCLList;
6040			pDCLCommand = (DCLCommandPtr) pDCLUpdateDCLList;
6041			pDCLUpdateDCLList->opcode = kDCLUpdateDCLListOp;
6042			pDCLUpdateDCLList->dclCommandList = pPlayBufferGroupData->bufferGroupUpdateDCLList;
6043			pDCLUpdateDCLList->numDCLCommands = pPlayBufferGroupData->updateListSize;
6044		}
6045
6046		// Loop to first buffer group.
6047		pDCLJump = (DCLJumpPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLJump));
6048		if( pDCLJump == nil )
6049		{
6050			error = memFullErr;
6051			goto bail;
6052		}
6053		pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLJump;
6054		pDCLCommand = (DCLCommandPtr) pDCLJump;
6055		pDCLJump->opcode = kDCLJumpOp;
6056		pDCLJump->pJumpDCLLabel = pLoopDCLLabel;
6057
6058		// Create label for underrun.
6059		pUnderrunDCLLabel = (DCLLabelPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLLabel));
6060		if( pUnderrunDCLLabel == nil )
6061		{
6062			error = memFullErr;
6063			goto bail;
6064		}
6065		pGlobalData->pUnderrunDCLLabel = pUnderrunDCLLabel;
6066		pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pUnderrunDCLLabel;
6067		pDCLCommand = (DCLCommandPtr) pUnderrunDCLLabel;
6068		pUnderrunDCLLabel->opcode = kDCLLabelOp;
6069
6070		// Set last buffer group's jump DCL to jump to underrun.
6071		pBufferGroupDCLJump->pJumpDCLLabel = pUnderrunDCLLabel;
6072
6073		// Call underrun proc.
6074		// This is the last command.
6075		pDCLCallProc = (DCLCallProcPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLCallProc));
6076		if( pDCLCallProc == nil )
6077		{
6078			error = memFullErr;
6079			goto bail;
6080		}
6081		pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLCallProc;
6082		pDCLCallProc->pNextDCLCommand = nil;
6083		pDCLCallProc->opcode = kDCLCallProcOp;
6084		pDCLCallProc->proc = handleDVOutputUnderrun;
6085		pDCLCallProc->procData = (UInt32) pDeviceDescription;
6086
6087		// Initialize number of active play packets.
6088		pGlobalData->activePackets = pGlobalData->totalPackets;
6089	}
6090
6091	// Set up all of the buffer groups.
6092	if (error == noErr)
6093	{
6094		pGlobalData->nextSYT = kPlaySYTDelay;
6095		pGlobalData->nextDBC = 0;
6096		pGlobalData->nextDataPacketNum = 0;
6097 		pGlobalData->pImageBuffer = nil;
6098		pPlayBufferGroupData = pGlobalData->pBufferGroupDataList;
6099		for (bufferGroupNum = 0; bufferGroupNum < kNumPlayBufferGroups; bufferGroupNum++)
6100		{
6101			updateDVOutputBuffers( (DCLCommandPtr) pPlayBufferGroupData->pBufferGroupDCLCallProc );
6102			pPlayBufferGroupData = pPlayBufferGroupData->pNextLocalData;
6103		}
6104	}
6105
6106	if (error == noErr)
6107		error = FWSetDCLProgramStart(pGlobalData->DCLProgramID, pGlobalData->pDCLList);
6108
6109// jkl, do we need to do this?
6110// 	if (error == noErr)
6111// 		error = FWSetDCLProgramStartEvent(pGlobalData->DCLProgramID, kFWDCLCycleEvent, 0, BitRange (12, 15));
6112
6113bail:
6114	// if we had a problem, lets clean it up
6115	if	(error != noErr)
6116	{
6117		disposeDCLOutput(pDeviceDescription->pGlobalDVOutData);
6118		pGlobalData = nil;
6119	}
6120
6121	return error;
6122}
6123
6124
6125////////////////////////////////////////////////////////////////////////////////
6126//
6127// DVHandleOutput
6128//
6129//   This routine handles isochronous sending of DV data.
6130//
6131
6132void handleDVOutput(DCLCommandPtr pDCLCommandPtr)
6133{
6134	DCLCallProcPtr				pDCLCallProc;
6135	DCLTimeStampPtr				pDCLTimeStamp;
6136	DVLocalOutPtr				pLocalData;
6137	DVGlobalOutPtr				pGlobalData;
6138	DVLocalOutPtr				pPrevLocalData;
6139	UInt32						nominalFrameCycleTime;
6140	UInt32						fractionalFrameCycleCount,
6141								fractionalFrameCycleOffset;
6142	SInt32						timeDrift;
6143	UInt32						cycleDrift;
6144	UInt32						projectedTimeStamp,
6145								projectedSYT;
6146
6147	// Recast pDCLCommandPtr.
6148	pDCLCallProc = (DCLCallProcPtr) pDCLCommandPtr;
6149
6150	// Get data for buffer group and driver data.
6151	pLocalData = (DVLocalOutPtr) pDCLCallProc->procData;
6152	pPrevLocalData = pLocalData->pPrevLocalData;
6153	pGlobalData = pLocalData->pGlobalData;
6154	nominalFrameCycleTime = pGlobalData->nominalFrameCycleTime;
6155
6156	// Undo skipping empty packet if we're currently skipping a packet.
6157	if (pLocalData->skippingEmptyPacket)
6158	{
6159		FWModifyDCLJump( pGlobalData->DCLProgramID, pLocalData->pBufferGroupSkipEmptyPacketDCLJump, pLocalData->pBufferGroupDontSkipEmptyPacketDCLLabel);
6160		pGlobalData->activePackets++;
6161		pLocalData->skippingEmptyPacket = false;
6162	}
6163
6164	// Compute time drift.
6165
6166	// Compute the projected time stamp value for the first packet of the current
6167	// buffer group the next time this proc is called for the current buffer group.
6168
6169	// Start at time stamp of first packet in next buffer group to be sent.
6170	pDCLTimeStamp = pLocalData->pBufferGroupDCLTimeStamp;
6171	projectedTimeStamp = pDCLTimeStamp->timeStamp;
6172 	projectedTimeStamp = addFWCycleTimeToFWCycleTime(projectedTimeStamp, 1 << 12);
6173
6174	// Add the total number of cycles for all active buffer group packets.
6175	projectedTimeStamp = addFWCycleTimeToFWCycleTime(projectedTimeStamp, pGlobalData->activePackets << 12);
6176
6177	// Subtract the number of cycles for all packets in the current buffer group.
6178	projectedTimeStamp = subtractFWCycleTimeFromFWCycleTime(projectedTimeStamp, pLocalData->numPackets << 12);
6179
6180	// Compute the projected SYT value for the first packet of the current buffer group
6181	// the next time this proc is called for the current buffer group.
6182
6183	// Start with the SYT value to use for the first packet of the next frame.
6184	projectedSYT = pGlobalData->nextSYT;
6185
6186	// Subtract the SYT offset between frames.
6187	projectedSYT = subtractFWCycleTimeFromFWCycleTime(projectedSYT, nominalFrameCycleTime);
6188
6189	// Add the fraction of the SYT offset between the start of the frame and the
6190	// first data packet for the current buffer group.
6191	fractionalFrameCycleOffset =
6192		((nominalFrameCycleTime & 0x0FFF) * pGlobalData->nextDataPacketNum) /
6193		pGlobalData->numDataPacketsPerFrame;
6194
6195	fractionalFrameCycleCount =
6196		((nominalFrameCycleTime & 0x01FFF000) * pGlobalData->nextDataPacketNum) /
6197		pGlobalData->numDataPacketsPerFrame;
6198	fractionalFrameCycleCount =
6199		(fractionalFrameCycleCount & 0x01FFF000) +
6200		(((fractionalFrameCycleCount & 0x0FFF) * 3072) / 4096);
6201
6202	projectedSYT = addFWCycleTimeToFWCycleTime (projectedSYT, fractionalFrameCycleOffset);
6203	projectedSYT = addFWCycleTimeToFWCycleTime (projectedSYT, fractionalFrameCycleCount);
6204
6205	// The time drift is the difference between the projected time stamp and SYT.
6206	// We must convert the time drift to cycles.
6207	cycleDrift = addFWCycleTimeToFWCycleTime(projectedTimeStamp, kPlaySYTDelay << 12);
6208	cycleDrift = subtractFWCycleTimeFromFWCycleTime(cycleDrift, projectedSYT);
6209	timeDrift = (cycleDrift >> 12) & 0x0F;
6210
6211	// Skip an empty packet if we're drifting.
6212	// Only consider positive drifting.
6213	if ((timeDrift > 0) && (timeDrift < 8))
6214	{
6215		FWModifyDCLJump (pGlobalData->DCLProgramID, pLocalData->pBufferGroupSkipEmptyPacketDCLJump, pLocalData->pBufferGroupSkipEmptyPacketDCLLabel);
6216		pGlobalData->activePackets--;
6217		pLocalData->skippingEmptyPacket = true;
6218	}
6219
6220	updateDVOutputBuffers( pDCLCommandPtr );
6221
6222	// Update DCL jumps to call underrun proc after this buffer group.
6223	//zzz check errors.
6224	FWModifyDCLJump (pGlobalData->DCLProgramID,
6225					 pLocalData->pEndOfBufferGroupDCLJump,
6226					 pGlobalData->pUnderrunDCLLabel);
6227	FWModifyDCLJump (pGlobalData->DCLProgramID,
6228					 pPrevLocalData->pEndOfBufferGroupDCLJump,
6229					 pPrevLocalData->pEndOfBufferGroupDCLLabel);
6230}
6231
6232////////////////////////////////////////////////////////////////////////////////
6233//
6234// DVUpdateOutputBuffers
6235//
6236//   This routine updates the buffers for sending DV data.
6237//
6238
6239void updateDVOutputBuffers(DCLCommandPtr pDCLCommandPtr)
6240{
6241	DCLCommandPtr				pCurrentDCLCommand;
6242	DCLCallProcPtr				pDCLCallProc;
6243	DCLTransferPacketPtr		pDCLTransferPacket;
6244	DVLocalOutPtr				pLocalData,
6245								pPrevLocalData;
6246	DVGlobalOutPtr				pGlobalData;
6247	UInt32						localNodeID;
6248	UInt32						generation;
6249	UInt32						nominalFrameCycleTime;
6250	UInt32						syt;
6251	UInt32						*pBuffer,
6252								*pImageBuffer,
6253								*pLastImageBuffer;
6254	UInt32						packetNum,
6255								dataPacketNum,
6256								numPackets;
6257	UInt32						dbc;
6258
6259	// Recast pDCLCommandPtr.
6260	pDCLCallProc = (DCLCallProcPtr) pDCLCommandPtr;
6261
6262	// Get data for buffer group.
6263	pLocalData = (DVLocalOutPtr) pDCLCallProc->procData;
6264
6265	// Get driver data and first DCL command.
6266	pGlobalData = pLocalData->pGlobalData;
6267	pCurrentDCLCommand = pLocalData->pFirstBufferGroupDCLCommand;
6268	nominalFrameCycleTime = pGlobalData->nominalFrameCycleTime;
6269
6270	// Get data for previous buffer group.
6271	pPrevLocalData = pLocalData->pPrevLocalData;
6272	syt = pGlobalData->nextSYT;
6273	dbc = pGlobalData->nextDBC;
6274	dataPacketNum = pGlobalData->nextDataPacketNum;
6275
6276	// Get local node ID.
6277	FWGetNodeID( pGlobalData->localFWReferenceID, &localNodeID, &generation);
6278
6279	// Get first send packet command for this buffer group.
6280	while (pCurrentDCLCommand->opcode != kDCLSendPacketStartOp)
6281		pCurrentDCLCommand = pCurrentDCLCommand->pNextDCLCommand;
6282	pDCLTransferPacket = (DCLTransferPacketPtr) pCurrentDCLCommand;
6283
6284	// Update the packet buffers.
6285	numPackets = pLocalData->numPackets;
6286
6287	// Get the next frame to output
6288 	if( pGlobalData->pImageBuffer == nil )
6289 		  getNextFullOutputFrame( pGlobalData->pDVFrameOutputData, &(pGlobalData->pImageBuffer) );
6290
6291 	pImageBuffer = ( pGlobalData->pImageBuffer + (kDVPacketQuadSize * dataPacketNum) );
6292	for( packetNum = 0; packetNum < numPackets; packetNum++)
6293	{
6294		// Set up packet header.
6295		pBuffer = (UInt32 *) pDCLTransferPacket->buffer;
6296		pBuffer[0] = 0x00780000 | (localNodeID << 24) | (dbc & 0xFF);
6297		if( pGlobalData->isNTSC )
6298			pBuffer[1] = 0x8000FFFF;
6299		else
6300			pBuffer[1] = 0x8080FFFF; // set PAL bit
6301
6302		// if not an empty packet
6303		if (pDCLTransferPacket->size == 488)
6304		{
6305			// Set SYT field if this is the first data packet in the frame.
6306			if (dataPacketNum == 0)
6307			{
6308				if( pGlobalData->isNTSC )
6309					pBuffer[1] = 0x80000000 | (syt & 0xFFFF);
6310				else
6311					pBuffer[1] = 0x80800000 | (syt & 0xFFFF); // PAL bit
6312
6313				syt = addFWCycleTimeToFWCycleTime(syt, pGlobalData->nominalFrameCycleTime);
6314			}
6315
6316			// Copy data into packet.
6317			BlockMoveData(pImageBuffer, pDCLTransferPacket->buffer + kDVPacketCIPSize, kDVPacketDataSize);
6318			pImageBuffer += 120;
6319			dbc++;
6320			dataPacketNum++;
6321
6322			// check if frame is done
6323			if (dataPacketNum == pGlobalData->numDataPacketsPerFrame )
6324			{
6325 				IDHDVCompleteEvent	event;
6326
6327				event.eventHeader.deviceID 	= pGlobalData->deviceID;
6328				event.eventHeader.event 	= kIDHPrivateEventWriteComplete;
6329				event.frameBuffer			= (Ptr) pImageBuffer;
6330 				event.fwCycleTime			= syt;
6331
6332				postEvent(
6333						pGlobalData->isochComponentGlobals,
6334						pGlobalData->deviceID,
6335						kIDHPrivateEventWriteComplete,
6336						(void*)&event);
6337
6338				dataPacketNum = 0;
6339				pLastImageBuffer = pGlobalData->pImageBuffer;
6340				getNextFullOutputFrame( pGlobalData->pDVFrameOutputData, &(pGlobalData->pImageBuffer) );
6341				pImageBuffer = pGlobalData->pImageBuffer;
6342
6343				// Mute the audio on repeating frames, based on repeating frame sequences
6344				if (pImageBuffer == pLastImageBuffer)
6345				{
6346					UInt32	i,j,k,n;
6347					UInt8	*tPtr;
6348
6349
6350					{
6351						// Get DSF flag in byte 3 of header (Blue Book p. 113)
6352						tPtr = (UInt8 *)pImageBuffer;
6353						if ((tPtr[3] &= 0x80) == 0)
6354							n=10;							// ntsc
6355						else
6356							n=12;							// pal
6357
6358						// Go thru the frame (10 times for ntsc, 12 for pal)
6359						for (i=0;i<n;i++)
6360						{
6361
6362							// Yet another attempt ...
6363							// Mute all the audio samples
6364
6365							for (i=0;i<n;i++)
6366							{
6367								for (j=0;j<9;j++)
6368								{
6369									tPtr = (UInt8 *)pImageBuffer + (i * 12000) + ((j * 16 + 6) * 80) + 8;
6370									for (k=0;k<72;k++)
6371										*tPtr++ = 0x0;
6372								}
6373
6374							}
6375						}
6376					}
6377				}
6378			}
6379		}
6380
6381
6382		// Find next send packet start command.
6383		pCurrentDCLCommand = pCurrentDCLCommand->pNextDCLCommand;
6384		while (pCurrentDCLCommand != nil)
6385		{
6386			if (pCurrentDCLCommand->opcode != kDCLSendPacketStartOp)
6387				pCurrentDCLCommand = pCurrentDCLCommand->pNextDCLCommand;
6388			else
6389				break;
6390		}
6391		pDCLTransferPacket = (DCLTransferPacketPtr) pCurrentDCLCommand;
6392	}
6393	pGlobalData->nextSYT = syt;
6394	pGlobalData->nextDBC = dbc;
6395	pGlobalData->nextDataPacketNum = dataPacketNum;
6396
6397	// Call to update DCL's if we need to.  We have to do this on underrun because the update
6398	// DCL command won't be run to reflect the changes we've made here.
6399	if (pLocalData->needsUpdate)
6400	{
6401		FWUpdateDCLList (pGlobalData->DCLProgramID, pLocalData->bufferGroupUpdateDCLList, pLocalData->updateListSize);
6402		pLocalData->needsUpdate = false;
6403	}
6404}
6405
6406////////////////////////////////////////////////////////////////////////////////
6407//
6408// DVHandleOutputUnderrun
6409//
6410//   This routine handles underruns for sending DV data.
6411//
6412
6413void handleDVOutputUnderrun(DCLCommandPtr pDCLCommandPtr)
6414{
6415	DCLCallProcPtr				pDCLCallProc;
6416	DeviceDescriptionPtr		pDeviceDescription;
6417	FWCommandObjectID			isochChannelCommandObjectID;
6418
6419	// Recast pDCLCommandPtr.
6420	pDCLCallProc = (DCLCallProcPtr) pDCLCommandPtr;
6421
6422	// Get driver data.
6423	pDeviceDescription = (DeviceDescriptionPtr) pDCLCallProc->procData;
6424
6425	// Set up params for stopping isochronous channel.
6426	isochChannelCommandObjectID = pDeviceDescription->pGlobalDVOutData->isochChannelCommandObjectID;
6427	FWSetFWCommandParams(isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, 0, handleDVOutputUnderrunCompletion, (UInt32) pDCLCallProc);
6428	FWSetIsochChannelCommandIsochChannelID(isochChannelCommandObjectID, pDeviceDescription->outputIsochChannelID);
6429
6430	// Stop the isochronous channel.
6431	FWStopIsochronousChannel(isochChannelCommandObjectID);
6432}
6433
6434
6435////////////////////////////////////////////////////////////////////////////////
6436//
6437// DVHandleOuputUnderrunCompletion
6438//
6439//   This routine handles underruns for sending DV data.
6440//
6441
6442void handleDVOutputUnderrunCompletion (
6443	FWCommandObjectID			fwCommandObjectID,
6444	OSStatus					commandStatus,
6445	UInt32						completionProcData)
6446{
6447	DCLCallProcPtr				pDCLCallProc;
6448	DeviceDescriptionPtr		pDeviceDescription;
6449	DVLocalOutPtr				pDVPlayBufferGroupData;
6450	FWCommandObjectID			isochChannelCommandObjectID;
6451	UInt32						bufferGroupNum;
6452
6453	// Get DCL call proc data.
6454	pDCLCallProc = (DCLCallProcPtr) completionProcData;
6455
6456	// Get driver data.
6457	pDeviceDescription = (DeviceDescriptionPtr) pDCLCallProc->procData;
6458
6459	// Reset next SYT, dbc, and data packet num.
6460	pDeviceDescription->pGlobalDVOutData->nextSYT = kPlaySYTDelay;
6461	pDeviceDescription->pGlobalDVOutData->nextDBC = 0;
6462	pDeviceDescription->pGlobalDVOutData->nextDataPacketNum = 0;
6463	pDeviceDescription->pGlobalDVOutData->pImageBuffer = nil;
6464
6465	// Reset up all of the buffer groups.
6466	pDVPlayBufferGroupData = pDeviceDescription->pGlobalDVOutData->pBufferGroupDataList;
6467	for (bufferGroupNum = 0; bufferGroupNum < kNumPlayBufferGroups; bufferGroupNum++)
6468	{
6469		// Update buffer group buffers.
6470		// jkl, no update needed, Lynx fwim and OHCI fwim don't do anything for this anyway
6471//		pDVPlayBufferGroupData->needsUpdate = true;
6472		updateDVOutputBuffers((DCLCommandPtr) pDVPlayBufferGroupData->pBufferGroupDCLCallProc);
6473
6474		// Update buffer group jump DCL.
6475		if (bufferGroupNum < (kNumPlayBufferGroups - 1))
6476		{
6477			FWModifyDCLJump (pDeviceDescription->pGlobalDVOutData->DCLProgramID,
6478							 pDVPlayBufferGroupData->pEndOfBufferGroupDCLJump,
6479							 pDVPlayBufferGroupData->pEndOfBufferGroupDCLLabel);
6480		}
6481		else
6482		{
6483			FWModifyDCLJump (pDeviceDescription->pGlobalDVOutData->DCLProgramID,
6484							 pDVPlayBufferGroupData->pEndOfBufferGroupDCLJump,
6485							 pDeviceDescription->pGlobalDVOutData->pUnderrunDCLLabel);
6486		}
6487
6488		pDVPlayBufferGroupData = pDVPlayBufferGroupData->pNextLocalData;
6489	}
6490
6491	// Set up params for starting isochronous channel.
6492	isochChannelCommandObjectID = pDeviceDescription->pGlobalDVOutData->isochChannelCommandObjectID;
6493	FWSetFWCommandParams (isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, 0, nil, 0);
6494	FWSetIsochChannelCommandIsochChannelID (isochChannelCommandObjectID, pDeviceDescription->outputIsochChannelID);
6495
6496	// Restart the isochronous channel.
6497	FWStartIsochronousChannel (isochChannelCommandObjectID);
6498}
6499
6500
6501
6502///////////////////////////////////////////////////////////////////////
6503//
6504// DVDisposeDCLOutput
6505//
6506///////////////////////////////////////////////////////////////////////
6507OSErr disposeDCLOutput( DVGlobalOutPtr pOutputData )
6508{
6509	DVLocalOutPtr				pLocalData,
6510								pNextLocalData;
6511	UInt32						bufferGroupNum;
6512	OSErr						error = noErr;
6513
6514	if( pOutputData != nil )
6515	{
6516		// Deallocate play buffer group data records.
6517		// and update lists associated with them
6518		pLocalData = pOutputData->pBufferGroupDataList;
6519		for (bufferGroupNum = 0; bufferGroupNum < kNumPlayBufferGroups; bufferGroupNum++)
6520		{
6521			if( pLocalData != nil )
6522			{
6523				pNextLocalData = pLocalData->pNextLocalData;
6524				dclDeallocatePlayBufferGroup (pLocalData);
6525				pLocalData = pNextLocalData;
6526			}
6527		}
6528
6529		if( pOutputData->pDCLCommandPool != nil )
6530			dclDeallocateCommandPool( pOutputData->pDCLCommandPool );
6531
6532		if( pOutputData->pDVFrameOutputData != nil )
6533			releaseDVFrameIO( pOutputData->pDVFrameOutputData );
6534
6535		FWDisposeDCLProgram( pOutputData->DCLProgramID );
6536
6537		if( pOutputData->pTransmitBuffers != nil )
6538			PoolDeallocate ( pOutputData->pTransmitBuffers );
6539
6540		if( pOutputData->isochChannelCommandObjectID != nil )
6541			FWDeallocateFWCommandObject ( pOutputData->isochChannelCommandObjectID );
6542
6543		PoolDeallocate ( pOutputData );
6544	}
6545	return( error );
6546}
6547
6548
6549//////////////////////////////////////////////////////////////////////////////////////
6550//////////////////////////////////////////////////////////////////////////////////////
6551// Utilitiy Routines
6552//////////////////////////////////////////////////////////////////////////////////////
6553//////////////////////////////////////////////////////////////////////////////////////
6554
6555
6556
6557///////////////////////////////////////////////////////////////////////////////////
6558// Buffer Allocation Routines
6559//   For Output
6560///////////////////
6561
6562////////////////////////////////////////////////////////////////////////////////
6563//
6564// DVCAllocatePlayBufferGroup
6565//
6566//   This routine allocates a buffer group for playing.
6567//
6568
6569DVLocalOutPtr dclAllocatePlayBufferGroup(DVGlobalOutPtr pGlobalData)
6570{
6571	DVLocalOutPtr				pLocalData = nil,
6572								pPrevLocalData,
6573								pNextLocalData;
6574	OSErr						error = noErr;
6575
6576	// Allocate buffer group data record.
6577	pLocalData = (DVLocalOutPtr) PoolAllocateResident(sizeof (DVLocalOut), true);
6578	if (pLocalData != nil)
6579		pLocalData->pGlobalData = pGlobalData;
6580	else
6581		error = memFullErr;
6582
6583	// Insert buffer group data record into list.
6584	if (error == noErr)
6585	{
6586		pNextLocalData = pGlobalData->pBufferGroupDataList;
6587		if (pNextLocalData != nil)
6588		{
6589			pPrevLocalData = pNextLocalData->pPrevLocalData;
6590
6591			pNextLocalData->pPrevLocalData = pLocalData;
6592			pLocalData->pNextLocalData = pNextLocalData;
6593		}
6594		else
6595		{
6596			pPrevLocalData = pLocalData;
6597
6598			pGlobalData->pBufferGroupDataList = pLocalData;
6599		}
6600
6601		pPrevLocalData->pNextLocalData = pLocalData;
6602		pLocalData->pPrevLocalData = pPrevLocalData;
6603	}
6604
6605	return pLocalData;
6606}
6607
6608
6609////////////////////////////////////////////////////////////////////////////////
6610//
6611// DVDeallocatePlayBufferGroup
6612//
6613//   This routine deallocates a buffer group for playing.
6614//
6615
6616void dclDeallocatePlayBufferGroup( DVLocalOutPtr	pLocalData )
6617{
6618	if (pLocalData != nil)
6619	{
6620		if (pLocalData->bufferGroupUpdateDCLList != nil)
6621			PoolDeallocate ((Ptr) pLocalData->bufferGroupUpdateDCLList);
6622
6623		PoolDeallocate ( pLocalData );
6624	}
6625}
6626
6627
6628////////////////////////////////////////////////////////////////////////////////
6629//
6630// DVCreatePlayBufferGroupUpdateList
6631//
6632//   This routine creates the update list for a play buffer group.
6633//
6634
6635OSErr createDVPlayBufferGroupUpdateList( DVLocalOutPtr pLocalData)
6636{
6637	DCLCommandPtr				pDCLCommand,
6638								pLastDCLCommand;
6639	DCLCommandPtr				*updateDCLList,
6640								*pUpdateDCLListEntry;
6641	UInt32						opcode;
6642	UInt32						updateListSize;
6643	OSErr						error = noErr;
6644
6645	// Loop through all DCL commands in buffer group and count all send packet DCL
6646	// commands.
6647	pDCLCommand = pLocalData->pFirstBufferGroupDCLCommand;
6648	pLastDCLCommand = pLocalData->pLastBufferGroupDCLCommand;
6649	updateListSize = 0;
6650	while (pDCLCommand != pLastDCLCommand)
6651	{
6652		opcode = pDCLCommand->opcode & ~kFWDCLOpFlagMask;
6653		if ((opcode == kDCLSendPacketStartOp) || (opcode == kDCLSendPacketOp))
6654			updateListSize++;
6655
6656		pDCLCommand = pDCLCommand->pNextDCLCommand;
6657	}
6658	opcode = pDCLCommand->opcode & ~kFWDCLOpFlagMask;
6659	if ((opcode == kDCLSendPacketStartOp) || (opcode == kDCLSendPacketOp))
6660		updateListSize++;
6661
6662	// Allocate update list.
6663	updateDCLList = (DCLCommandPtr *)
6664		PoolAllocateResident (updateListSize * sizeof (DCLCommandPtr), false);
6665	if (updateDCLList == nil)
6666		error = memFullErr;
6667
6668	// Loop through all DCL commands in buffer group and add all send packet DCL
6669	// commands to update list.
6670	if (error == noErr)
6671	{
6672		pDCLCommand = pLocalData->pFirstBufferGroupDCLCommand;
6673		pLastDCLCommand = pLocalData->pLastBufferGroupDCLCommand;
6674		pUpdateDCLListEntry = updateDCLList;
6675
6676		while (pDCLCommand != pLastDCLCommand)
6677		{
6678			opcode = pDCLCommand->opcode & ~kFWDCLOpFlagMask;
6679			if ((opcode == kDCLSendPacketStartOp) || (opcode == kDCLSendPacketOp))
6680				*pUpdateDCLListEntry++ = pDCLCommand;
6681
6682			pDCLCommand = pDCLCommand->pNextDCLCommand;
6683		}
6684
6685		opcode = pDCLCommand->opcode & ~kFWDCLOpFlagMask;
6686		if ((opcode == kDCLSendPacketStartOp) || (opcode == kDCLSendPacketOp))
6687			*pUpdateDCLListEntry++ = pDCLCommand;
6688	}
6689
6690	// Save update list.
6691	if (error == noErr)
6692	{
6693		pLocalData->bufferGroupUpdateDCLList = updateDCLList;
6694		pLocalData->updateListSize = updateListSize;
6695	}
6696	else
6697	{
6698		pLocalData->bufferGroupUpdateDCLList = nil;
6699		pLocalData->updateListSize = 0;
6700	}
6701
6702	return ( error );
6703}
6704
6705
6706////////////////////////////////////////////////////////////////////////////////////
6707// DCL Allocation Routines
6708//
6709//  We run our own allocation engine because of the sheer number of DCLs. The command
6710// pool holds larger blocks out of which we allocate the DCL commands, this takes
6711// some work out of the hands of the memory manager.
6712//
6713///////////////////
6714
6715////////////////////////////////////////////////////////////////////////////////
6716//
6717// AllocateDCLCommand
6718//
6719//   This routine allocates a DCL command.
6720//
6721
6722DCLCommandPtr dclAllocateCommand(DCLCommandPoolPtr pDCLCommandPool, UInt32 dclSize)
6723{
6724	DCLCommandBlockPtr			pDCLCommandBlock;
6725	DCLCommandPtr				pDCLCommand;
6726
6727	// Get last DCL command block in pool.
6728	pDCLCommandBlock = pDCLCommandPool->pLastDCLCommandBlock;
6729
6730	// Check if we have enough room in command block.  Allocate a new block if there
6731	// is not enough room.
6732	if (pDCLCommandBlock != nil)
6733	{
6734		if( dclSize > (kDCLCommandPoolBlockSize - pDCLCommandBlock->blockLevel) )
6735			pDCLCommandBlock = dclAllocateCommandBlock( pDCLCommandPool );
6736	}
6737	else
6738	{
6739		pDCLCommandBlock = dclAllocateCommandBlock ( pDCLCommandPool );
6740	}
6741
6742	// Allocate a DCL from the command block.
6743	if (pDCLCommandBlock != nil)
6744	{
6745		pDCLCommand =
6746			(DCLCommandPtr) (pDCLCommandBlock->block + pDCLCommandBlock->blockLevel);
6747		pDCLCommandBlock->blockLevel += dclSize;
6748	}
6749	else
6750	{
6751		pDCLCommand = nil;
6752	}
6753
6754	return pDCLCommand;
6755}
6756
6757
6758////////////////////////////////////////////////////////////////////////////////
6759//
6760// AllocateDCLCommandBlock
6761//
6762//   This routine allocates a DCL command block.
6763//
6764
6765DCLCommandBlockPtr dclAllocateCommandBlock( DCLCommandPoolPtr pDCLCommandPool )
6766{
6767	DCLCommandBlockPtr			pDCLCommandBlock = nil;
6768	Ptr							block;
6769	OSStatus					error = noErr;
6770
6771	// Allocate command block record.
6772	pDCLCommandBlock =
6773		(DCLCommandBlockPtr) PoolAllocateResident( sizeof(DCLCommandBlock), true );
6774	if( pDCLCommandBlock == nil )
6775		error = memFullErr;
6776
6777	// Allocate command block block.
6778	if( error == noErr )
6779	{
6780		block = PoolAllocateResident( kDCLCommandPoolBlockSize, false );
6781		if (block != nil)
6782			pDCLCommandBlock->block = block;
6783		else
6784			error = memFullErr;
6785	}
6786
6787	// Insert command block into pool.
6788	if( error == noErr )
6789	{
6790		if( pDCLCommandPool->pFirstDCLCommandBlock != nil )
6791			pDCLCommandPool->pLastDCLCommandBlock->pNextDCLCommandBlock = pDCLCommandBlock;
6792		else
6793			pDCLCommandPool->pFirstDCLCommandBlock = pDCLCommandBlock;
6794
6795		pDCLCommandPool->pLastDCLCommandBlock = pDCLCommandBlock;
6796		pDCLCommandBlock->pNextDCLCommandBlock = nil;
6797	}
6798
6799	// Clean up on error.
6800	if( ( error != noErr ) && ( pDCLCommandBlock != nil ) )
6801	{
6802		dclDeallocateCommandBlock( pDCLCommandBlock );
6803		pDCLCommandBlock = nil;
6804	}
6805
6806	// Return results.
6807	return( pDCLCommandBlock );
6808}
6809
6810
6811////////////////////////////////////////////////////////////////////////////////
6812//
6813// DeallocateDCLCommandBlock
6814//
6815//   This routine deallocates a DCL command block.
6816//
6817
6818void dclDeallocateCommandBlock( DCLCommandBlockPtr pDCLCommandBlock )
6819{
6820	if( pDCLCommandBlock != nil )
6821	{
6822		// Deallocate command block block.
6823		if( pDCLCommandBlock->block != nil )
6824			PoolDeallocate( pDCLCommandBlock->block );
6825
6826		// Deallocate command block record.
6827		PoolDeallocate( (Ptr) pDCLCommandBlock );
6828
6829		//zzz if there were any errors...?
6830	}
6831}
6832
6833////////////////////////////////////////////////////////////////////////////////
6834//
6835// AllocateDCLCommandPool
6836//
6837//   This routine allocates a DCL command pool.
6838//
6839
6840DCLCommandPoolPtr dclAllocateCommandPool( void )
6841{
6842	DCLCommandPoolPtr			pDCLCommandPool;
6843
6844	// Allocate DCL command pool record.
6845	pDCLCommandPool = (DCLCommandPoolPtr) PoolAllocateResident( sizeof( DCLCommandPool ), true );
6846
6847	return (pDCLCommandPool);
6848}
6849
6850////////////////////////////////////////////////////////////////////////////////
6851//
6852// DeallocateDCLCommandPool
6853//
6854//   This routine deallocates a DCL command pool.
6855//
6856
6857void dclDeallocateCommandPool( DCLCommandPoolPtr pDCLCommandPool )
6858{
6859	DCLCommandBlockPtr			pDCLCommandBlock,
6860								pNextDCLCommandBlock;
6861
6862	if( pDCLCommandPool != nil )
6863	{
6864		// Deallocate all command blocks.
6865		pDCLCommandBlock = pDCLCommandPool->pFirstDCLCommandBlock;
6866		while( pDCLCommandBlock != nil )
6867		{
6868			pNextDCLCommandBlock = pDCLCommandBlock->pNextDCLCommandBlock;
6869			dclDeallocateCommandBlock( pDCLCommandBlock );
6870			pDCLCommandBlock = pNextDCLCommandBlock;
6871		}
6872
6873		// Deallocate command pool record.
6874		PoolDeallocate ((Ptr) pDCLCommandPool);
6875		//zzz if there was an error...?
6876	}
6877}
6878
6879
6880////////////////////////////////////////////////////////////////////////////////
6881//
6882// AddFWCycleTimeToFWCycleTime
6883//
6884//   This routine adds the two given cycle times.
6885//
6886
6887UInt32	addFWCycleTimeToFWCycleTime( UInt32 cycleTime1, UInt32 cycleTime2 )
6888{
6889	UInt32						secondCount,
6890								cycleCount,
6891								cycleOffset;
6892	UInt32						cycleTime;
6893
6894	// Add cycle offsets.
6895	cycleOffset = (cycleTime1 & 0x0FFF) + (cycleTime2 & 0x0FFF);
6896
6897	// Add cycle counts.
6898	cycleCount = (cycleTime1 & 0x01FFF000) + (cycleTime2 & 0x01FFF000);
6899
6900	// Add any carry over from cycle offset to cycle count.
6901	if (cycleOffset > 3071)
6902	{
6903		cycleCount += 0x1000;
6904		cycleOffset -= 3072;
6905	}
6906
6907	// Add secondCounts.
6908	secondCount = (cycleTime1 & 0xFE000000) + (cycleTime2 & 0xFE000000);
6909
6910	// Add any carry over from cycle count to secondCount.
6911	if (cycleCount > (7999 << 12))
6912	{
6913		secondCount += 0x02000000;
6914		cycleCount -= (8000 << 12);
6915	}
6916
6917	// Put everything together into cycle time.
6918	cycleTime = secondCount | cycleCount | cycleOffset;
6919
6920	return (cycleTime);
6921}
6922
6923
6924////////////////////////////////////////////////////////////////////////////////
6925//
6926// SubtractFWCycleTimeFromFWCycleTime
6927//
6928//   This routine subtracts the two given cycle times.
6929//
6930
6931UInt32 subtractFWCycleTimeFromFWCycleTime( UInt32 cycleTime1, UInt32 cycleTime2 )
6932{
6933	SInt32						secondCount,
6934								cycleCount,
6935								cycleOffset;
6936	UInt32						cycleTime;
6937
6938	// Subtract cycle offsets.
6939	cycleOffset = (cycleTime1 & 0x0FFF) - (cycleTime2 & 0x0FFF);
6940
6941	// Subtract cycle counts.
6942	cycleCount = (cycleTime1 & 0x01FFF000) - (cycleTime2 & 0x01FFF000);
6943
6944	// Subtract any borrow over from cycle offset to cycle count.
6945
6946	if (cycleOffset < 0)
6947	{
6948		cycleCount -= 0x1000;
6949		cycleOffset += 3072;
6950	}
6951
6952	// Subtract secondCounts.
6953	secondCount = (cycleTime1 & 0xFE000000) - (cycleTime2 & 0xFE000000);
6954
6955	// Subtract any borrow over from cycle count to secondCount.
6956	if (cycleCount < 0)
6957	{
6958		secondCount -= 0x02000000;
6959		cycleCount += (8000 << 12);
6960	}
6961
6962	// Put everything together into cycle time.
6963	cycleTime = secondCount | cycleCount | cycleOffset;
6964
6965	return (cycleTime);
6966}
6967
6968
6969////////////////////////////////////////////////////////////////////////////////
6970//
6971// ConvertFractionalSecondsToFWCycleTime
6972//
6973//   This routine converts a time represented in fractional seconds to a time
6974// represented in cycle timer format.
6975//
6976
6977UInt32	convertFractionalSecondsToFWCycleTime( UInt32 secondsNumerator, UInt32 secondsDenominator )
6978{
6979	float						fSecondCount;
6980	float						fCycleCount;
6981	float						fCycleOffset;
6982	UInt32						iSecondsCount;
6983	UInt32						iCycleCount;
6984	UInt32						iCycleOffset;
6985	UInt32						secondsCycleTime;
6986
6987	// Convert fractional seconds into floating point and compute seconds count.
6988	fSecondCount = ((float) secondsNumerator) / ((float) secondsDenominator);
6989	iSecondsCount = (UInt32) fSecondCount;
6990
6991	// Subtract whole seconds out of fSecondCount and convert to cycle count.
6992	fCycleCount = (fSecondCount - ((float) iSecondsCount)) * 8000.0;
6993	iCycleCount = (UInt32) fCycleCount;
6994
6995	// Subtract whole cycles out of fCycleCount and convert to cycle offset.
6996	fCycleOffset = (fCycleCount - ((float) iCycleCount)) * 3072.0;
6997	iCycleOffset = (UInt32) fCycleOffset;
6998
6999	// Convert to cycle timer format.
7000	secondsCycleTime = (iSecondsCount << 25) | (iCycleCount << 12) | iCycleOffset;
7001
7002	return (secondsCycleTime);
7003}
7004
7005//////////////////////////////////////////////////////////////////////
7006// DVGetNextFullOutputFrame
7007//
7008// Returns next frame full of data for output
7009//
7010
7011OSErr getNextFullOutputFrame( DVIODataPtr pOData, UInt32** ppFrame )
7012{
7013	OSErr						error = noErr;
7014	QElemPtr					pQElem;
7015
7016	// dequeue an element from the output list
7017	error = PBDequeueFirst( pOData->pFullQ, &pQElem);
7018
7019	// if we got a frame
7020	if( error == noErr )
7021	{
7022		// put old frame on empty queue
7023		error = PBEnqueueLast( pOData->pCurFrameElem, pOData->pEmptyQ );
7024		//zzz what if we can't enqueue
7025
7026		// save new frame element
7027		pOData->pCurFrameElem = pQElem;
7028
7029		// return the actual frame
7030		*ppFrame = ((DVFrameQElemPtr)pQElem)->pFrame;
7031	}
7032	else
7033		// return old frame
7034		*ppFrame = ((DVFrameQElemPtr)pOData->pCurFrameElem)->pFrame;
7035
7036	return( error );
7037}
7038
7039///////////////////////////////////////////////////////////////////////
7040//
7041// DVGetNextEmptyOutputFrame
7042//
7043///////////////////////////////////////////////////////////////////////
7044
7045OSErr getNextEmptyOutputFrame( DVIODataPtr pOData, UInt32** ppFrame)
7046{
7047	OSErr						error = noErr;
7048	DVFrameQElemPtr				pQElem;
7049
7050	// dequeue an element from the empty list
7051	error = PBDequeueFirst( pOData->pEmptyQ, (QElemPtr *) &pQElem );
7052
7053	// if we got an element
7054	if( error == noErr )
7055	{
7056		// put the frame on the busy queue
7057		error = PBEnqueueLast( (QElemPtr) pQElem, pOData->pBusyQ );
7058		//zzz and if we get an error
7059
7060		// return the frame
7061		*ppFrame = pQElem->pFrame;
7062	}
7063	else
7064		*ppFrame = nil;
7065
7066	return( error );
7067}
7068
7069///////////////////////////////////////////////////////////////////////
7070//
7071// DVQueueNextFullOutputFrame
7072//
7073///////////////////////////////////////////////////////////////////////
7074
7075OSErr queueNextFullOutputFrame( DVIODataPtr pOData, UInt32* pFrame )
7076{
7077	OSStatus					error = noErr;
7078	QElemPtr					pCurElem;
7079
7080	// find frame in busy queue
7081	pCurElem = pOData->pBusyQ->qHead;
7082	while( pCurElem != nil)
7083	{
7084		// is it the one we want?
7085		if( ((DVFrameQElemPtr)pCurElem)->pFrame == pFrame )
7086			break;
7087		else
7088			// next
7089			pCurElem = pCurElem->qLink;
7090	}
7091
7092	// if we found it
7093	if( pCurElem != nil )
7094	{
7095		// get the element from the busy queue
7096		error = PBDequeue( pCurElem, pOData->pBusyQ );
7097
7098		// and put it on the full queue
7099		if( error == noErr )
7100			error = PBEnqueueLast( pCurElem, pOData->pFullQ );
7101	}
7102	else
7103		error = kDVFrameNotFoundErr;
7104
7105	return( error );
7106}
7107
7108//////////////////////////////////////////////////////////////////////
7109// DVGetNextEmptyInputFrame
7110//
7111// Returns next empty frame for input
7112//
7113
7114OSErr getNextEmptyInputFrame( DVIODataPtr pIData, UInt32** ppFrame )
7115{
7116	OSErr						error = noErr;
7117	QElemPtr					pQElem;
7118
7119	// dequeue an element from the output list
7120	error = PBDequeueFirst( pIData->pEmptyQ, &pQElem);
7121
7122	if( error != noErr )
7123	{
7124		error = PBDequeueFirst( pIData->pFullQ, &pQElem);
7125	}
7126
7127	// if we got a frame
7128	if( error == noErr )
7129	{
7130		// put old frame on full queue
7131		error = PBEnqueueLast( pIData->pCurFrameElem, pIData->pFullQ );
7132		//zzz what if we can't enqueue
7133
7134		// save new frame element
7135		pIData->pCurFrameElem = pQElem;
7136
7137		// return the actual frame
7138		*ppFrame = ((DVFrameQElemPtr)pQElem)->pFrame;
7139	}
7140	else
7141		// return old frame
7142		*ppFrame = ((DVFrameQElemPtr)pIData->pCurFrameElem)->pFrame;
7143
7144	return( error );
7145}
7146
7147//////////////////////////////////////////////////////////////////////
7148//
7149// DVGetNextFullInputFrame
7150//
7151// Returns next full frame of data for input
7152//
7153
7154OSErr getNextFullInputFrame( DVIODataPtr pIData, UInt32** ppFrame )
7155{
7156	OSErr						error = noErr;
7157	DVFrameQElemPtr				pQElem;
7158
7159	// dequeue an element from the full list
7160	error = PBDequeueFirst( pIData->pFullQ, (QElemPtr *) &pQElem );
7161
7162	// if we got an element
7163	if( error == noErr )
7164	{
7165		// put the frame on the busy queue
7166		error = PBEnqueueLast( (QElemPtr) pQElem, pIData->pBusyQ );
7167		//zzz and if we get an error
7168
7169		// return the frame
7170		*ppFrame = pQElem->pFrame;
7171	}
7172	else
7173		*ppFrame = nil;
7174
7175	return( error );
7176}
7177
7178///////////////////////////////////////////////////////////////////////
7179//
7180// DVReleaseFullInputFrame
7181//
7182///////////////////////////////////////////////////////////////////////
7183
7184OSErr releaseFullInputFrame( DVIODataPtr pIData, UInt32* pFrame )
7185{
7186	OSStatus					error = noErr;
7187	QElemPtr					pCurElem;
7188
7189	// find frame in busy queue
7190	pCurElem = pIData->pBusyQ->qHead;
7191	while( pCurElem != nil)
7192	{
7193		// is it the one we want?
7194		if( ((DVFrameQElemPtr)pCurElem)->pFrame == pFrame )
7195			break;
7196		else
7197			// next
7198			pCurElem = pCurElem->qLink;
7199	}
7200
7201	// if we found it
7202	if( pCurElem != nil )
7203	{
7204		// get the element from the busy queue
7205		error = PBDequeue( pCurElem, pIData->pBusyQ );
7206
7207		// and put it on the empty queue
7208		if( error == noErr )
7209			error = PBEnqueueLast( pCurElem, pIData->pEmptyQ );
7210	}
7211	else
7212		error = kDVFrameNotFoundErr;
7213
7214	return( error );
7215}
7216
7217//////////////////////////////////////////////////////////////////////
7218//
7219// DVGetCurrentDCLFrame
7220//
7221
7222Boolean isDVFinished( DVIODataPtr pIOData )
7223{
7224	Boolean		result = false;
7225
7226	if( pIOData != nil )
7227	{
7228		if( pIOData->pFullQ->qHead == nil )
7229			result = true;
7230		else
7231			result = false;
7232	}
7233	else
7234		result = false;
7235
7236	return( result );
7237}
7238
7239//////////////////////////////////////////////////////////////////////
7240//
7241// DVGetCurrentDCLFrame
7242//
7243
7244OSErr getCurrentDCLFrame( DVIODataPtr pIOData, UInt32** ppFrame )
7245{
7246	OSErr						error = noErr;
7247
7248	if( pIOData != nil )
7249	{
7250		// get the current frame
7251		*ppFrame = ((DVFrameQElemPtr)pIOData->pCurFrameElem)->pFrame;
7252	}
7253	else
7254	{
7255		error = kDVFrameNotFoundErr;
7256		*ppFrame = nil;
7257	}
7258	return( error );
7259}
7260
7261///////////////////////////////////////////////////////////////////////
7262//
7263// DVInitFrameIO
7264//
7265
7266OSErr initDVFrameInput(UInt32 standard, DVIODataPtr* ppIOData)
7267{
7268	OSErr				error = noErr;
7269	DVIODataPtr			pIOData;
7270	DVFrameQElemPtr		pQElem[kNumInputDVFrames];
7271	UInt32				i;
7272	UInt32				frameSize;
7273
7274	// create output data struct
7275	pIOData = (DVIODataPtr) PoolAllocateResident( sizeof( DVIOData ), true );
7276
7277	if( pIOData == nil )
7278	{
7279		error = memFullErr;
7280		goto bail;
7281	}
7282
7283	// create busy queue
7284	error = PBQueueCreate( &(pIOData->pBusyQ) );
7285	if( error != noErr )
7286		goto bail;
7287
7288	error = PBQueueInit( pIOData->pBusyQ );
7289	if( error != noErr )
7290		goto bail;
7291
7292	// create empty queue
7293	error = PBQueueCreate( &(pIOData->pEmptyQ) );
7294	if( error != noErr )
7295		goto bail;
7296
7297	error = PBQueueInit( pIOData->pEmptyQ );
7298	if( error != noErr )
7299		goto bail;
7300
7301
7302	// create full queue
7303	error = PBQueueCreate( &(pIOData->pFullQ) );
7304	if( error != noErr )
7305		goto bail;
7306
7307	error = PBQueueInit( pIOData->pFullQ );
7308	if( error != noErr )
7309		goto bail;
7310
7311	for(i = 0; i < kNumInputDVFrames; i++)
7312	{
7313		// create queue elements
7314		pQElem[i] = (DVFrameQElemPtr) PoolAllocateResident( sizeof(DVFrameQElem), true );
7315		if ( pQElem[i] == nil )
7316		{
7317			error = memFullErr;
7318			goto bail;
7319		}
7320
7321		// create frames
7322		if (standard == ntscIn)
7323			frameSize = kNTSCCompressedBufferSize;
7324		else
7325			frameSize = kPALCompressedBufferSize;
7326
7327		pQElem[i]->pFrame = (UInt32 *) PoolAllocateResident( frameSize, true );
7328		if ( pQElem[i]->pFrame == nil )
7329		{
7330			error = memFullErr;
7331			goto bail;
7332		}
7333	}
7334
7335	// set current frame to start
7336	pIOData->pCurFrameElem = (QElemPtr) pQElem[0];
7337
7338	// insert the rest of the queue elements into empty queue
7339	for(i = 1; i < kNumInputDVFrames; i++ )
7340	{
7341		error = PBEnqueueLast( (QElemPtr) pQElem[i], pIOData->pEmptyQ );
7342	}
7343
7344bail:
7345	if( error != noErr )
7346	{
7347		releaseDVFrameIO( pIOData );
7348		pIOData = nil;
7349	}
7350
7351	*ppIOData = pIOData;
7352
7353	return( error );
7354}
7355
7356///////////////////////////////////////////////////////////////////////
7357//
7358// initDVFrameOutput
7359//
7360OSErr initDVFrameOutput(UInt32 standard, DVIODataPtr* ppIOData)
7361{
7362	OSErr				error = noErr;
7363	DVIODataPtr			pIOData;
7364	DVFrameQElemPtr		pQElem[kNumOutputDVFrames];
7365	UInt32				i;
7366	UInt32				frameSize;
7367
7368	// create output data struct
7369	pIOData = (DVIODataPtr) PoolAllocateResident(sizeof(DVIOData), true);
7370
7371	if( pIOData == nil )
7372	{
7373		error = memFullErr;
7374		goto bail;
7375	}
7376
7377	// create busy queue
7378	error = PBQueueCreate( &(pIOData->pBusyQ) );
7379	if( error != noErr )
7380		goto bail;
7381
7382	error = PBQueueInit( pIOData->pBusyQ );
7383	if( error != noErr )
7384		goto bail;
7385
7386	// create empty queue
7387	error = PBQueueCreate( &(pIOData->pEmptyQ) );
7388	if( error != noErr )
7389		goto bail;
7390
7391	error = PBQueueInit( pIOData->pEmptyQ );
7392	if( error != noErr )
7393		goto bail;
7394
7395	// create full queue
7396	error = PBQueueCreate( &(pIOData->pFullQ) );
7397	if( error != noErr )
7398		goto bail;
7399
7400	error = PBQueueInit( pIOData->pFullQ );
7401	if( error != noErr )
7402		goto bail;
7403
7404	for(i = 0; i < kNumOutputDVFrames; i++)
7405	{
7406		// create queue elements
7407		pQElem[i] = (DVFrameQElemPtr) PoolAllocateResident( sizeof(DVFrameQElem), true );
7408		if ( pQElem[i] == nil )
7409		{
7410			error = memFullErr;
7411			goto bail;
7412		}
7413
7414		// create frames
7415		if (standard == ntscIn)
7416			frameSize = kNTSCCompressedBufferSize;
7417		else
7418			frameSize = kPALCompressedBufferSize;
7419
7420		pQElem[i]->pFrame = (UInt32 *) PoolAllocateResident( frameSize, true );
7421		if ( pQElem[i]->pFrame == nil )
7422		{
7423			error = memFullErr;
7424			goto bail;
7425		}
7426	}
7427
7428	// set current frame to start
7429	pIOData->pCurFrameElem = (QElemPtr) pQElem[0];
7430
7431	// insert the rest of the queue elements into empty queue
7432	for(i = 1; i < kNumOutputDVFrames; i++ )
7433	{
7434		error = PBEnqueueLast( (QElemPtr) pQElem[i], pIOData->pEmptyQ );
7435	}
7436
7437bail:
7438	if( error != noErr )
7439	{
7440		releaseDVFrameIO( pIOData );
7441		pIOData = nil;
7442	}
7443
7444	*ppIOData = pIOData;
7445
7446	return( error );
7447}
7448
7449///////////////////////////////////////////////////////////////////////
7450//
7451// DVDeleteFrameAndElement
7452//
7453///////////////////////////////////////////////////////////////////////
7454
7455static void deleteDVFrameAndElement( DVFrameQElemPtr pQElem )
7456{
7457	// deallocate frame
7458	// checks just to be safe
7459	if( pQElem != nil )
7460	{
7461		if( pQElem->pFrame != nil )
7462			PoolDeallocate( pQElem->pFrame );
7463
7464		// deallocate queue element
7465		PoolDeallocate( pQElem );
7466
7467		//zzz never check errors
7468	}
7469}
7470
7471///////////////////////////////////////////////////////////////////////
7472//
7473// DVReleaseFrameIO
7474//
7475///////////////////////////////////////////////////////////////////////
7476
7477OSErr releaseDVFrameIO( DVIODataPtr pIOData )
7478{
7479	OSErr						error = noErr;
7480	QElemPtr					pQElem;
7481
7482	if( pIOData != nil )
7483	{
7484		if( pIOData->pBusyQ != nil )
7485		{
7486			// dequeue and delete all elements in the busy queue
7487			do {
7488				error = PBDequeueFirst( pIOData->pBusyQ, (QElemPtr *) &pQElem );
7489
7490				if( error == noErr )
7491					deleteDVFrameAndElement( (DVFrameQElemPtr) pQElem );
7492
7493			} while( error == noErr );
7494
7495			error = PBQueueDelete( pIOData->pBusyQ );
7496		}
7497
7498
7499		if( pIOData->pEmptyQ != nil )
7500		{
7501			// dequeue and delete all elements in the empty queue
7502			do {
7503				error = PBDequeueFirst( pIOData->pEmptyQ, (QElemPtr *) &pQElem );
7504
7505				if( error == noErr )
7506					deleteDVFrameAndElement( (DVFrameQElemPtr) pQElem );
7507
7508			} while( error == noErr );
7509
7510			error = PBQueueDelete( pIOData->pEmptyQ );
7511		}
7512
7513
7514		if( pIOData->pFullQ != nil )
7515		{
7516			// dequeue and delete all elements in the full queue
7517			do {
7518				error = PBDequeueFirst( pIOData->pFullQ, (QElemPtr *) &pQElem );
7519
7520				if( error == noErr )
7521					deleteDVFrameAndElement( (DVFrameQElemPtr) pQElem );
7522
7523			} while( error == noErr );
7524
7525			error = PBQueueDelete( pIOData->pFullQ );
7526		}
7527
7528		// delete current frame and element
7529		if( pIOData->pCurFrameElem != nil )
7530			deleteDVFrameAndElement( (DVFrameQElemPtr) pIOData->pCurFrameElem );
7531
7532		// dump the main guy
7533		PoolDeallocate( pIOData );
7534	}
7535
7536	return( error );
7537}
7538
7539
7540///////////////////////////////////////////////////////////////////////
7541//
7542// DVInitDCLInput
7543//
7544///////////////////////////////////////////////////////////////////////
7545
7546OSErr dclInitInput(DeviceDescriptionPtr pDeviceDescription,
7547		IsochComponentGlobals* isochComponentGlobals )
7548{
7549	Ptr							pReceiveBuffer;
7550	Ptr							pDCLCommand;
7551	DVGlobalInPtr				pGlobalData;
7552	DCLLabelPtr					pStartDCLLabel;
7553	DVLocalInPtr				pLocalData;
7554	DCLCommandPtr				*updateDCLList,
7555								*startUpdateDCLList;
7556	DCLUpdateDCLListPtr			pDCLUpdateDCLList;
7557	DCLTransferPacketPtr		pDCLTransferPacket;
7558	DCLCallProcPtr				pDCLCallProc;
7559	DCLJumpPtr					pDCLInputLoop;
7560	UInt32						packetNum;
7561	UInt32						bufferNum;
7562	UInt32						updateListSize;
7563	OSErr						error = noErr;
7564
7565	// Create the global data structure
7566	pGlobalData = PoolAllocateResident( sizeof(DVGlobalIn), true );
7567	if( pGlobalData == nil )
7568		error = memFullErr;
7569	else
7570		pDeviceDescription->pGlobalDVInData = pGlobalData;
7571
7572	pGlobalData->isochComponentGlobals = isochComponentGlobals;
7573	pGlobalData->deviceID = pDeviceDescription->deviceID;
7574	pGlobalData->pDCLList = nil;
7575	pGlobalData->ppUpdateDCLList = nil;
7576	pGlobalData->pDVFrameInputData = nil;
7577
7578	// Create input buffer. Add slop for page alignment
7579	pGlobalData->pReceiveBuffers = PoolAllocateResident(kInputBufferSize + 4096, false);
7580	if( pGlobalData->pReceiveBuffers == nil )
7581	{
7582		error = memFullErr;
7583		goto bail;
7584	}
7585	pReceiveBuffer = pGlobalData->pReceiveBuffers;
7586
7587	// Align buffer, should be aligned already
7588	pReceiveBuffer = (Ptr) ((((UInt32) pReceiveBuffer) + 4096) & 0xfffff000);
7589
7590	#ifdef dbg_in_init
7591		ASSERTVAR( error, "Error initializing input DCL data structures.  Error: %d" );
7592	#endif
7593
7594	// init frame IO
7595	error = initDVFrameInput(pDeviceDescription->standard, &pGlobalData->pDVFrameInputData);
7596	if( error != noErr )
7597		goto bail;
7598
7599	error = getCurrentDCLFrame( pGlobalData->pDVFrameInputData, (UInt32 **) &(pGlobalData->pImageBuffer) );
7600	if( error != noErr )
7601		goto bail;
7602
7603	#ifdef dbg_in_init
7604		ASSERTVAR( error, "Error initializing input DCL frame I/O system.  Error: %d" );
7605	#endif
7606
7607	// Fill out global structure
7608	pGlobalData->isNTSC = (pDeviceDescription->standard == ntscIn);
7609	pGlobalData->packetCount = 0;
7610
7611	error = FWCreateDCLProgram( &(pGlobalData->DCLProgramID) );
7612	pDeviceDescription->dclProgramID = pGlobalData->DCLProgramID;
7613	if( error != noErr )
7614		goto bail;
7615
7616	// create list of DCLs for channel
7617	// make sure we zero it so deallocation can work even if not fully initialized
7618	pGlobalData->pDCLList = (DCLCommandPtr) PoolAllocateResident( kDCLReadProgramSize, true );
7619	if ( pGlobalData->pDCLList == nil )
7620	{
7621		error = memFullErr;
7622		goto bail;
7623	}
7624
7625	// create list of update DCLs for channel
7626	// make sure we zero it so deallocation can work even if not fully initialized
7627	pGlobalData->ppUpdateDCLList = (DCLCommandPtr *) PoolAllocateResident( kNumInputDCLs * sizeof (DCLCommandPtr), true );
7628	if ( pGlobalData->ppUpdateDCLList == nil )
7629	{
7630		error = memFullErr;
7631		goto bail;
7632	}
7633
7634	#ifdef dbg_in_init
7635		DEBUGVAR( "Initialized input DCL commands.  Error: %d", error );
7636	#endif
7637
7638	// Get pointer to start of DCL commands and update list.
7639	pDCLCommand = (Ptr) pGlobalData->pDCLList;
7640	updateDCLList = pGlobalData->ppUpdateDCLList;
7641
7642	// Create label for start of loop.
7643	pStartDCLLabel = (DCLLabelPtr) pDCLCommand;
7644	pDCLCommand += sizeof( DCLLabel );
7645	pStartDCLLabel->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
7646	pStartDCLLabel->opcode = kDCLLabelOp;
7647
7648
7649	// Create input buffer lists of 100 packets each.
7650	for (bufferNum = 0; bufferNum < kNumInputBuffers; bufferNum++)
7651	{
7652		// Create the DCL input record record and fill it in.
7653		pLocalData = (DVLocalInPtr) PoolAllocateResident( sizeof(DVLocalIn), true );
7654		if( pLocalData == nil )
7655		{
7656			error = memFullErr;
7657			goto bail;
7658		}
7659		pLocalData->pFirstCmd = (DCLCommandPtr) pDCLCommand;
7660		pLocalData->pGlobalData = pGlobalData;
7661
7662		startUpdateDCLList = updateDCLList;
7663		updateListSize = 0;
7664		// Create transfer DCL for each packet.
7665		for (packetNum = 0; packetNum < kNumPacketsPerInputBuffer; packetNum++)
7666		{
7667			// Receive one packet up to kReceiveDVPacketSize bytes.
7668			pDCLTransferPacket = (DCLTransferPacketPtr) pDCLCommand;
7669			pDCLCommand += sizeof (DCLTransferPacket);
7670			pDCLTransferPacket->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
7671			pDCLTransferPacket->opcode = kDCLReceivePacketStartOp;
7672			pDCLTransferPacket->buffer = pReceiveBuffer;
7673			pDCLTransferPacket->size = kReceiveDVPacketSize;
7674
7675			*updateDCLList++ = (DCLCommandPtr) pDCLTransferPacket;
7676			updateListSize++;
7677			pReceiveBuffer += kAlignedDVPacketSize;
7678		}
7679
7680		// Create update DCL list.
7681		pDCLUpdateDCLList = (DCLUpdateDCLListPtr) pDCLCommand;
7682		pDCLCommand += sizeof (DCLUpdateDCLList);
7683		pDCLUpdateDCLList->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
7684		pDCLUpdateDCLList->opcode = kDCLUpdateDCLListOp;
7685		pDCLUpdateDCLList->dclCommandList = startUpdateDCLList;
7686		pDCLUpdateDCLList->numDCLCommands = updateListSize;
7687
7688		// Call the DVStorePackets proc.
7689		pDCLCallProc = (DCLCallProcPtr) pDCLCommand;
7690		pDCLCommand += sizeof (DCLCallProc);
7691		pDCLCallProc->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
7692		pDCLCallProc->opcode = kDCLCallProcOp;
7693		pDCLCallProc->proc = storeDVPackets;
7694		pDCLCallProc->procData = (UInt32) pLocalData;
7695	}
7696
7697
7698	#ifdef dbg_in_init
7699		DEBUGVAR( "Created input DCL program.  Error: %d", error );
7700	#endif
7701
7702	// Loop to start of ping pong.
7703	pDCLInputLoop = (DCLJumpPtr) pDCLCommand;
7704	pDCLInputLoop->pNextDCLCommand = nil;
7705	pDCLInputLoop->opcode = kDCLJumpOp;
7706	pDCLInputLoop->pJumpDCLLabel = pStartDCLLabel;
7707
7708	// Set start of DCL program.
7709	error = FWSetDCLProgramStart (pGlobalData->DCLProgramID, pGlobalData->pDCLList);
7710
7711bail:
7712	if( error != noErr )
7713	{
7714		disposeDCLInput( pGlobalData );
7715		pGlobalData = nil;
7716	}
7717
7718	return( error );
7719}
7720
7721///////////////////////////////////////////////////////////////////////
7722//
7723// DVStorePackets(
7724//
7725///////////////////////////////////////////////////////////////////////
7726
7727void storeDVPackets( DCLCommandPtr pDCLCommandPtr )
7728{
7729	DCLCallProcPtr				pDCLCallProc;
7730	DVLocalInPtr				pLocalData;
7731	DVGlobalInPtr				pGlobalData;
7732	DCLCommandPtr				pCurrentCmd;
7733	DCLTransferPacketPtr		pDCLTransferPacket;
7734	Ptr							pPacketBuffer;
7735	UInt32						packetHeader, packetSize, packetNum, packetPerFrame;
7736	Boolean						vSyncDetected;
7737	UInt8						currentSequenceCount;
7738
7739	// Recast DCL command.
7740	pDCLCallProc = (DCLCallProcPtr) pDCLCommandPtr;
7741
7742	// Get input data.
7743	pLocalData = (DVLocalInPtr) pDCLCallProc->procData;
7744	pGlobalData = pLocalData->pGlobalData;
7745
7746	// Get info from ping pong data.
7747	pCurrentCmd = pLocalData->pFirstCmd;
7748
7749	// How many packets we talkin'?
7750	packetPerFrame = pGlobalData->isNTSC ? kNumPacketsPerNTSCFrame : kNumPacketsPerPALFrame;
7751
7752	for ( packetNum = 0; packetNum < kNumPacketsPerInputBuffer; packetNum++ )
7753	{
7754		// compute size of transfer
7755		pDCLTransferPacket = (DCLTransferPacketPtr) pCurrentCmd;
7756		pPacketBuffer = pDCLTransferPacket->buffer;
7757		packetHeader = *((UInt32*) pPacketBuffer);
7758		pPacketBuffer += 4; // 4 byte 1394 header
7759		packetSize = (packetHeader & kFWIsochDataLength) >> kFWIsochDataLengthPhase;
7760
7761		// detect vSync
7762		if( packetSize > 8 )
7763			vSyncDetected = ((*(short *)(pPacketBuffer + 8)  & 0xE0F8 ) == 0x0000 );
7764		else
7765			vSyncDetected = false;
7766
7767		// get current data block sequence counter value and increment saved value
7768		// saved value will be decremented at the end for null packets which do not
7769		// increment data block count
7770		currentSequenceCount = pPacketBuffer[3];
7771		pGlobalData->lastSequenceCount++;
7772
7773		// skip over CIP header
7774		pPacketBuffer += 8;  // 8 bytes
7775		packetSize -= 8;
7776
7777// RecordEventLogger( 'isoc', 'cnt ', pGlobalData->lastSequenceCount, currentSequenceCount);
7778
7779		if( vSyncDetected )
7780		{
7781			if( pGlobalData->packetCount == packetPerFrame )		// if we got our frameSync at the right time
7782			{
7783				IDHDVCompleteEvent event;
7784
7785				// post a DV event to let the curious know...
7786 				event.eventHeader.deviceID	= pGlobalData->deviceID;
7787 				event.eventHeader.event 	= kIDHPrivateEventReadComplete;
7788 				event.frameBuffer			= (Ptr) pGlobalData->pImageBuffer;
7789 				event.bufferSize			= pGlobalData->packetCount * kDVPayloadPacketSize;
7790				event.fwCycleTime			= (packetHeader & kFWIsochSy) >> kFWIsochSyPhase;
7791
7792				postEvent(
7793						pGlobalData->isochComponentGlobals,
7794						pGlobalData->deviceID,
7795						kIDHPrivateEventReadComplete,
7796						(void*)&event);
7797
7798				getNextEmptyInputFrame( pGlobalData->pDVFrameInputData, (UInt32 **) &(pGlobalData->pImageBuffer) ); 	// get a new frame
7799			}
7800
7801			pGlobalData->packetCount = 0;							// start a new frame
7802
7803			if ((packetSize == kDVPayloadPacketSize) && (currentSequenceCount == pGlobalData->lastSequenceCount))
7804			{
7805				// store the packet
7806				BlockMoveData
7807				(
7808					pPacketBuffer,
7809					(Ptr)((UInt32) pGlobalData->pImageBuffer + (pGlobalData->packetCount * kDVPayloadPacketSize)),
7810					packetSize
7811				);
7812
7813				pGlobalData->packetCount++;
7814			}
7815		}
7816		else
7817		{
7818			if (currentSequenceCount == pGlobalData->lastSequenceCount)
7819			{
7820				if ((pGlobalData->packetCount < packetPerFrame) && (packetSize == kDVPayloadPacketSize))
7821				{
7822					// store the packet
7823					BlockMoveData
7824					(
7825						pPacketBuffer,
7826						(Ptr)((UInt32) pGlobalData->pImageBuffer + (pGlobalData->packetCount * kDVPayloadPacketSize)),
7827						packetSize
7828					);
7829
7830					pGlobalData->packetCount++;
7831				}
7832				else if ((pGlobalData->packetCount >= packetPerFrame) && (packetSize > 0))
7833				{
7834					// too many packets between vSync detection, start new frame
7835					pGlobalData->packetCount = 0;
7836
7837					RecordEventLogger( 'isoc', 'stor', 0, '2man');
7838
7839				}
7840			}
7841			else
7842			{
7843				// packet out of sequence, start new frame
7844				pGlobalData->packetCount = 0;
7845
7846				RecordEventLogger( '****', 'seq!', currentSequenceCount, pGlobalData->lastSequenceCount);
7847			}
7848		}
7849
7850		// if null packet, decrement saved count since DBC is not incremented
7851		// otherwise set last count to current count to resynch counts if bad sequence
7852		if (packetSize == 0)	// already deceremented CIP header from packet size
7853			pGlobalData->lastSequenceCount--;
7854		else if( packetSize == kDVPayloadPacketSize)
7855			pGlobalData->lastSequenceCount = currentSequenceCount;
7856		else
7857		{
7858			pGlobalData->lastSequenceCount--;
7859			RecordEventLogger( '****', 'wird', pGlobalData->lastSequenceCount, packetSize);
7860//			DebugStr( "\pWeird packet");
7861		}
7862
7863		// update for next packet
7864		pCurrentCmd = pCurrentCmd->pNextDCLCommand;
7865	}
7866}
7867
7868
7869///////////////////////////////////////////////////////////////////////
7870//
7871// DVDisposeDCLInput
7872//
7873///////////////////////////////////////////////////////////////////////
7874
7875OSErr disposeDCLInput( DVGlobalInPtr pInputData )
7876{
7877	Ptr							pDCLCommand;
7878	DCLCallProcPtr				pDCLPingPongProc;
7879	short						bufferNum;
7880	OSErr						error = noErr;
7881
7882	if( pInputData != nil )
7883	{
7884		// deallocate receive buffer
7885		if (pInputData->pReceiveBuffers != nil)
7886			PoolDeallocate(pInputData->pReceiveBuffers);
7887
7888		// traverse through the DCL program to get to where
7889		// the buffers are and deallocate them
7890		if( pInputData->pDCLList != nil )
7891		{
7892			pDCLCommand = (Ptr) pInputData->pDCLList + sizeof (DCLLabel);
7893
7894			// for all call procs
7895			for (bufferNum = 0; bufferNum < kNumInputBuffers; bufferNum++)
7896			{
7897				// find the call proc
7898				pDCLCommand += sizeof (DCLTransferPacket) * kNumPacketsPerInputBuffer +
7899						sizeof (DCLUpdateDCLList);
7900
7901				// dealloc the call proc
7902				pDCLPingPongProc = (DCLCallProcPtr) pDCLCommand;
7903				pDCLCommand += sizeof (DCLCallProc);
7904				if( pDCLPingPongProc->procData != nil )
7905					PoolDeallocate( (LogicalAddress) pDCLPingPongProc->procData );
7906			}
7907
7908			PoolDeallocate( pInputData->pDCLList );
7909		}
7910
7911		// release update list
7912		if( pInputData->ppUpdateDCLList != nil )
7913			error = PoolDeallocate( pInputData->ppUpdateDCLList );
7914
7915		// release frame IO
7916		if( pInputData->pDVFrameInputData != nil )
7917			error = releaseDVFrameIO( pInputData->pDVFrameInputData );
7918
7919		// dispose the program
7920		error = FWDisposeDCLProgram( pInputData->DCLProgramID );
7921
7922		// and the whole bloody structure
7923		PoolDeallocate( pInputData );
7924	}
7925	return( error );
7926}
7927
7928OSErr writeFrame(IsochComponentInstancePtr ih, char *tmpBuffPtr)
7929{
7930	DeviceDescriptionPtr		pDeviceDescription;
7931	OSErr						error = noErr;
7932
7933	error = findDeviceDescriptionforDevice(ih, ih->deviceID, &pDeviceDescription);
7934
7935	if (error == noErr)
7936		error = queueNextFullOutputFrame(pDeviceDescription->pGlobalDVOutData->pDVFrameOutputData, (UInt32 *) tmpBuffPtr);
7937	FailWithVal( error != noErr, Exit, error);
7938
7939Exit:
7940	return error;
7941}
7942
7943OSErr getEmptyOutputFrame(IsochComponentInstancePtr ih, Ptr *tmpBuffPtr, UInt32 *size)
7944{
7945	DeviceDescriptionPtr		pDeviceDescription;
7946	OSErr						error = noErr;
7947
7948	error = findDeviceDescriptionforDevice(ih, ih->deviceID, &pDeviceDescription);
7949	FailWithVal( error != noErr, Exit, error);
7950
7951	*size = (pDeviceDescription->standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize;
7952
7953	error = getNextEmptyOutputFrame(pDeviceDescription->pGlobalDVOutData->pDVFrameOutputData, (UInt32 **) tmpBuffPtr);
7954	FailWithVal( error != noErr, Exit, error);
7955
7956Exit:
7957	return error;
7958}
7959
7960//====================================================================================
7961//
7962// registerDevice()
7963//	Register the device and install the FireWire port callback routines and bus reset proc.
7964//
7965//====================================================================================
7966OSStatus registerDevice(
7967		RegEntryID* inRegEntryID,
7968		FWDriverID* outDriverID,
7969		DeviceDescriptionPtr pDeviceDescription)
7970{
7971	CSRROMEntryID csrROMEntryID;
7972	OSStatus err = noErr;
7973
7974	err = FWRegisterDriver(
7975			inRegEntryID,
7976			outDriverID,
7977			&csrROMEntryID,
7978			(UInt32) pDeviceDescription);
7979
7980	if (err)
7981		goto Exit;
7982
7983	err = FWSetFWClientInitIsochPortProc(*outDriverID, initIsochPort);
7984	if (err)
7985		goto Exit;
7986
7987	err = FWSetFWClientStartIsochPortProc(*outDriverID, startIsochPort);
7988	if (err)
7989		goto Exit;
7990
7991	err = FWSetFWClientStopIsochPortProc(*outDriverID, stopIsochPort);
7992	if (err)
7993		goto Exit;
7994
7995	err = FWSetFWClientReleaseIsochPortProc(*outDriverID, releaseIsochPort);
7996
7997Exit:
7998	return err;
7999}
8000
8001// handle any reset notifications for the device
8002OSStatus handleBusReset(FWClientInterfaceParamsPtr pParams, UInt32 *pCommandAcceptance)
8003{
8004	DeviceDescriptionPtr pDeviceDescription;
8005	OSErr error = noErr;
8006
8007	pDeviceDescription = (DeviceDescriptionPtr) pParams->fwClientSpecificData;
8008
8009	RecordEventLogger( 'bus ', 'res ', (UInt32) pDeviceDescription->fwClientID, 0);
8010
8011	FWClientCommandIsComplete(pParams->fwClientCommandID, error);
8012	*pCommandAcceptance = kFWClientCommandAcceptNoMore;
8013
8014	error = postEvent( pDeviceDescription->componentGlobals, pDeviceDescription->deviceID, kIDHEventDeviceChanged, nil);
8015
8016	return error;
8017}
8018