DVIsochComponent.MacOS9   [plain text]


/*
	File:		DVIsochComponent.c

	Contains:	DV version of the Isoch component

	Version:	1.0

	Copyright:	й 1999-2000 by Apple Computer, Inc., all rights reserved.

	File Ownership:

		DRI:				Richard Sepulveda

		Other Contact:		Jay Lloyd, Kevin Williams

		Technology:			DV

	Writers:

		(CK)	Casey King
		(SF)	Sean Findley
		(KW)	Kevin Williams
		(jkl)	Jay Lloyd
		(GDW)	George D. Wilson Jr.
		(SW)	Sean Williams
		(RS)	Richard Sepulveda

	Change History (most recent first):

	  <DV86>	 3/16/00	SF		[2438610] Put 1-second delay before checking camera
									configuration.
	  <DV85>	 2/14/00	CK		[2432944]  A little more to kill this bug <sf,cp>
	  <DV84>	 2/10/00	SF		[2432944] Add retries to bandwidth/channel allocations.
	  <DV83>	 12/7/99	jkl		Changed isocMedia to isochMedia.
	  <DV82>	 11/1/99	jkl		Added useCMP atom for turning on and off CMP. Moved CMP from
									device specific to global. Moved the check for useCMP in the
									resource to global allocate time. Changed CMPWrite to update the
									expectedValue after each attempt to try to make it succeed. Some
									time the Canon camera arbitrarily fails on this. This needs to
									be redone so the entire operation can make sure the values are
									correct.
	  <DV81>	10/25/99	KW		If an error occurs during one of the calls to FSL during
									cmpResetStage1..9, set the reset stage to kNoResetsProcessing.
									This ensures that the next bus reset will be processed.
	  <DV80>	10/22/99	KW		In updateDeviceList, do not zero out localFWReferenceID when a
									device is removed. Zeroing out the localFWReferenceID causes the
									cmp bus reset routines to stop propogating. In doAVCTransaction
									deallocated the command object in the event of an error.
	  <DV79>	 9/28/99	KW		In IDHRead and IDHWrite, do not tear down read/write
									notification if an error occurs.
	  <DV78>	 9/21/99	jkl		Backed out this read/write postEvent deferred stuff. A
									stack-based variable was killing it and it needs more analysis.
	  <DV77>	 9/20/99	jkl		Really made the previous change. The DVFamilyLib postEvent was
									commented as meaning to defer the rest of the read/write
									complete notification to another deferred task, but was calling
									CallSecondaryInterruptHandler2 to do it. Since the postEvents
									for the read/write complete are already at deferred task, this
									call basically did nothing. Now QueueSecondaryInterruptHandler
									is used which definitely postpones the completion of postEvent
									to another deferred task. This is now much more of a risky
									change.
		<76>	 9/17/99	jkl		Changed isoch read/write complete notification callProcs to be
									called in another SIH.
		<75>	 8/29/99	RS		Fixed problem with unfriendly clients who wouldn't cancel their
									notifications before closing the isoch component. This could
									cause a crash if we callback and the client isn't there anymore.

		<74>	 8/28/99	KW		Dispose of all clients signed up for notifications when last
									instance is closed. Fixes crashes for hot plugging.
		<73>	 8/17/99	jkl		Changed CMP check to look for file type of INIT instead of gdfx.
		<72>	 8/16/99	RS		Added isoch version atom to device list.
		<71>	 8/16/99	jkl		Changed useCMP function to be off by default and look for a
									'ucmp' resource id -20756 before using CMP. FireWire 2.2 or
									greater check is still there.
		<70>	 8/16/99	jkl		Replicated a default DV name atom property in old name property
									location. Moved the bus reset notification proc with FireWire to
									only be active during the isoch read and write. This makes sure
									an instance is always open with the call proc address valid.
		<69>	 8/12/99	jkl		Bracketed the name QTCopyAtomToPtr with lock/unlock container
									calls.
		<68>	 8/10/99	jkl		Added camera name updating routine.
		<67>	  8/9/99	RS		Added IDHUpdateDeviceList() to API. The client can now change
									r/w atoms in the internal device list. This currently includes
									device name. Completed the bus reset generated IDHDeviceChanged
									notification. The client can now be notified at interrupt time
									whether a device state has been changed.
		<66>	  8/9/99	jkl		Added handleBusReset call proc for interrupt level bus reset
									notification.
		<65>	  8/9/99	jkl		Added some camera name stuff. Moved the name atom to be a direct
									child of the device atom. Default name is DV, if a name has been
									entered for the device then use it, else if the vendor is known
									then name is "vendor name DV".
		<64>	  8/6/99	jkl		More work to get rid of DVFamily.h. Added defines for
									getDeviceStandard AVC transaction. Also changed default camera
									name to DV and left off standard.
		<63>	  8/6/99	jkl		Changed kNTSCStandard to be ntscIn from QTComponents.h.
		<62>	  8/3/99	RS		Properly handling device disconnect by propogating errors to
									client(s).
		<61>	 7/29/99	jkl		Set input channel when camera is listener during bus reset
									handling. Some cameras do not follow the spec. Made sure to set
									bus reset processing state to noProcessing after bus reset
									processing has been interrupted.
		<60>	 7/28/99	jkl		Added all of the bus reset stages for CMP. Still not robust
									enough to handle errors during bus reset. Added Gestalt check
									for FireWire 2.2 or greater. CMP does not run if this check
									fails, allowing HaveBlue to run on FireWire 2.1.
		<59>	 7/28/99	jkl		Changed FWRegisterDriver to use the device description instead
									of driver refNum for client specific data. This lets the bus
									reset handling get the device description. Nobody was using the
									refNum. Turned on bus reset management.
		<58>	 7/28/99	jkl		Moved isochPortCommandObject and asynchCommandObject allocation
									to updateDeviceList, device added routine. Set max payload for
									asynch command object to 512. Changed all of the cmp routines to
									use the already allocated asynch command object. Changed local
									pcrBuffer modifications to always use FWRead and
									FWCompareAndSwap. Reduced overhead ID from 14 to 12.
		<57>	 7/23/99	jkl		Turned off CMP bus reset handling.
		<56>	 7/22/99	jkl		CMP, added correct speed, bandwidth, and overheadID calculations
									and configuration to manage reconnections after bus reset.
		<55>	 7/20/99	jkl		Fixed CMP bug where device not broadcasting could look
									connected. Turned off broadcast bit to disconnect plug before
									changing channel on a device only broadcasting. Sony is not
									going to like this but it solves compatibility problems and
									strictly follows the spec.
		<54>	 7/20/99	jkl		Turned CMP on. Can create a file in Preferences folder called
									"no cmp" to disable CMP.
		<53>	 7/18/99	jkl		Fixed command object allocation in irmAllocateBandwidth. Fixed
									initial overhead ID value for output plug control register.
		<52>	 7/16/99	jkl		Added latest CMP code.
		<51>	 7/16/99	KW		Moved FireWire callbacks to here. Register all devices with
									FireWire that have ever been seen when the first isoch component
									is opened. Likewise, Unregister all devices when the last isoch
									component is closed. Ensure that the device notification ID for
									a device is zeroed out after the notification has been disposed
									of.
		<50>	 7/14/99	jkl		Fixed dclProgramID mistake.
		<49>	 7/14/99	jkl		Made sure isochPortCommandObjects are allocated safely and not
									prematurely deallocated. Made sure disable read and write are
									safe if extraneous calls are made to them.
		<48>	 7/13/99	jkl		Reverted to previous version. Added fix for device control
									getTimeCode failing after hot plug.
		<47>	 7/12/99	jkl		Added a bunch of CMP stuff. Almost ready to turn on.
		<46>	 7/12/99	RS		Changed component instance storage from system heap to
									application heap to work around misbehaved apps that don't
									CloseComponent.
		<45>	 7/10/99	RS		Added notification for device added/removed so that we can
									update the fwClientID in the device control component if the
									camera is removed then re-added.
		<44>	  7/8/99	RS		Fixed problem where the device control instance was being
									prematurely released before all of the clients were finished.
	  <OX43>	  7/7/99	SW		Just removed some unused variables to stop compiler warnings.
		<42>	  7/7/99	jkl		Cleared driverRefNum and fwClientID fields on device removed,
									and updated them on device added for an already known device.
									Also updated localFWReferenceID in case the same camera is
									plugged into a different FireWire interface on the machine.
		<41>	  7/5/99	jkl		Added SetMaxPayload for client.
		<40>	  7/2/99	RS		Replaced event recording with logger to increase debug
									capability.
		<39>	  7/1/99	RS		Passing actual device description pointer instead of client
									instance to isoch callbacks since the client can change deviceID
									behind our back.
		<38>	 6/30/99	RS		Fixed error codes returned in certain functions. Removed
									openDeviceConnection() function because it is no long needed.
									Fixed bug with device name atom. Fixed bug where we were
									disabling read/writes in Close() if device was already possibly
									disabled.
		<37>	 6/28/99	RS		Moved IDHGetClientID from public to private call. Added
									kIDHErrDeviceTimeout errors for sync read/writes timeouts.
									Created public structs for IDHDimension and IDHResolution. Added
									deviceID to atom tree.
		<36>	 6/28/99	RS		Moved open device control from OpenDevice to SetConfig so that
									client doesn't have to Open a device for read/write in order to
									device control. Added device enable/disable notification.
									Returning error on getConfig if no previous setConfig.
		<35>	 6/27/99	jkl		Added default to NTSC in getDeviceStandard for devices that may
									not have any device control capability.
		<34>	 6/26/99	jkl		Integrated Richard's, Kevin's, and my changes to get read and
									write working. Wired clock back up. Too much other stuff to
									mention. Compare revisions if you really want to know.
		<33>	 6/24/99	RS		Added the DVCWriteFrame() and DVGetNextEmptyFrame() equivalent.
									We think all of the plumbing is in. So let the fun debug begin.
		<32>	 6/24/99	KW		Not ready for prime time but things compile. Errors exist.
		<31>	 6/24/99	jkl		Yanked all of the DV driver code into here. Some fun.
	  <OX30>	 6/22/99	SW		Add support for private calls to the IDH DV component instead of
									just using morphed GetDeviceStatus() calls.
		<29>	 6/21/99	RS		Added timeouts to synchronous reads and writes. The timeout has
									initially been set to 1/15 second (2 frame times).
		<28>	 6/20/99	RS		Moved device atom to device description structure to correctly
									handle device added/removed issues. Added PAL audio format
									config. Initialize device atom seed to 1. More cleanup.
		<27>	 6/19/99	RS		Removed getting connectionID in IDHGetDeviceStatus (not needed).
		<26>	 6/19/99	RS		Added deviceAtom and device standard to device description
									structure. Allocating correct buffer size according to camera
									format (ntsc or pal). Fixed bug where deviceID could change with
									last connection going to zero (BAD). Alot of general cleanup.
		<25>	 6/18/99	RS		Added kIDHUniqueIDType to device atom tree. Added some auxillary
									functions to help traverse atom list.
		<24>	 6/18/99	GDW		Made device control component handling global verses per
									instance.
		<23>	 6/18/99	GDW		Made some changes for additional device control component.
		<22>	 6/17/99	RS		Made some changes to open and close device to handle device
									access a little more gracefully.
		<21>	 6/17/99	RS		Added FWClientID to device descriptor for device control. Fixed
									bug when groing from write to read mode on a camera. Buffers
									weren't being allocated.
		<20>	 6/17/99	jkl		Added fwClientID to DVGetLocalFWReferenceID call to make the
									fwClientID part of the device info so IDH can execute
									getDeviceStandard by itself.
		<19>	 6/16/99	RS		Added Firewire Node ID to device structure. Added AVC
									transaction code to isoch component to work around DVDriver hang
									problem. Check for sync I/O problem when called at non-task
									time.
		<18>	 6/17/99	GDW		Added more device control calls.
		<17>	 6/16/99	RS		Do parameter checking on all public IDH functions. Turn on
									exclusive access checking. Fix bug with I/O direction atom size.
		<16>	 6/16/99	GDW		Add calls to device control component.
		<15>	 6/15/99	RS		Updating device list everytime a device is added or removed.
									Locking down atom container when we are copying data from atom
									to pointer.
		<14>	 6/15/99	RS		Added synchronous I/O and passing a 'nil' buffer into IDHWrite.
		<13>	 6/15/99	RS		Changed IODirection on DV configs from input to InputAndOutput.
		<12>	 6/15/99	RS		Merged DVFamilyLib into Isoch component. Added seed for device
									list validity check from client. Create component globals in
									register and free in unregister. Check seed when client does a
									SetConfig.
	  <OX11>	 6/11/99	SW		MasterInterfaces integration
		<10>	 6/10/99	RS		Added two new IDH functions ReleaseBuffer and CancelIO. Added
									notification version of Reads and Writes. MAJOR CHANGES.
		 <9>	  6/8/99	RS		Fixed bug storing IDHDeviceStatus in atom list.
		 <8>	  6/7/99	RS		Keep track of open for reads/writes per device.
		 <7>	  6/7/99	RS		Saving open permissions in component instance. Replaced Fail
									macro with new macro that displays function return code (better
									for debug). Disable device on close based on permissions flags.
									Returning bufferlist on read/write completion callback. Fixed
									bug in IDHWrite where we weren't doing a DVCWriteFrame.
	   	 <6>	  6/4/99	SW		Adjust for slight changes in IDH.i
		 <5>	  6/1/99	RS		Added private functions to find device atom given any of its
									siblings and a function to get device status given a device ID.
									Added device standard and firewire node ID to status struct.
									Save open permissions in device status.
		 <4>	 5/24/99	RS		Changed param type in SetDeviceConfig. Allow user to get device
									status on any device (not just configured device). Changed
									IDHIsochReadData and IDHIsochWriteData to IDHReadData and
									IDHWrite.
		 <3>	 5/24/99	RS		We are now returning a copy of device descrip atom container.
		 <2>	 5/20/99	RS		Added queueing of isoch reads and write. Changed error return
									code names.
*/

/*							еее	TO DO еее

	camera tracking
	if a camera goes offline, do we return an error from getdevicestatus or set the active flag
	super find atom
	turn on permanent locks by client
	review atom list (i.e. isoch container atom)
*/

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Includes

#include <DeviceControl.h>
#include <DeviceControlPriv.h>
#include <DriverServices.h>
#include <Gestalt.h>
#include <Resources.h>
#include <Sound.h>
#include <TextUtils.h>
#include <DeviceControl.h>
#include <IsochronousDataHandler.h>
#include <IsochronousDataHandlerPriv.h>
#include <Folders.h>

#include <fp.h>
#include <stddef.h>
#include <stdio.h>

#include "DVVers.h"
#include "FireWire.h"
#include "GenericDriverFamily.h"
#include "LoggerAndDebugMacros.h"
#include "OxcartDebug.h"
#include "DVDCLOut.h"
#include "DVDCLIn.h"

#define kDVDeviceInfo 				'ddin'	// DV device info
#define	kBuffersPerDevice			5		// number of buffers per device for reads and writes
#define kMaxDevicesActive			64		// max devices on firewire bus
#define kMaxInstances				10		// max clients attached to a device
#define kMaxNotifications			100		// 100 notifcaionts can be supported
#define kNTSCCompressedBufferSize	120000	
#define	kPALCompressedBufferSize	144000	
#define kIDHSeedAtomType			'seed'	// seed for device atom validity
#define kIDHDevicePresentType		'pres'	// is device present?
#define	kServiceTypeDVServices		'avc '
#define kTimeoutDuration			(1000 / 15) * durationMillisecond	// 1/15 second (2 frame times)
#define kMaxRetries					8

#if PRAGMA_ALIGN_SUPPORTED
#pragma options align=mac68k
#endif

#define kNoResetsProcessing 	0
#define kResetProcessing		1
#define kResetInterrupted		2
#define kResetFailed			3

#define koPCRaddrHi		0x0000FFFF
#define koPCRaddrLo		0xF0000904
#define kiPCRaddrHi		0x0000FFFF
#define kiPCRaddrLo		0xF0000984

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// globals
ComponentInstance gCurInstance = 0;

typedef struct IsochComponentInstance	IsochComponentInstance, 
										*IsochComponentInstancePtr;										
typedef struct IsochComponentGlobals 	IsochComponentGlobals, 
										*IsochComponentGlobalsPtr, 
										**IsochComponentGlobalsHandle;


struct IDHDVCompleteEvent {
	IDHEventHeader		eventHeader;
	Ptr					frameBuffer;
	UInt32				bufferSize;
	UInt32				fwCycleTime;

};
typedef struct IDHDVCompleteEvent IDHDVCompleteEvent;


// internal frame queue structure
typedef struct QueueElement {
	struct QElem 	*qLink;									// standard queue vars
	short			qType;
	short			locks;									// number of clients owning buffer
	Ptr				buffer;									// the data
	long			size;									// size of the data
	} QueueElement, *QueueElementPtr;

// describes a device on the firewire bus
struct DeviceDescription {
	IsochComponentGlobals	*componentGlobals;				// isoch component globals	
	QHdrPtr					freeQueue,						// holds free buffers
							usedQueue;						// holds buffers being used by client
	IDHDeviceID				deviceID;						// persistent unique ID for each device
	FWReferenceID			fwClientID;						// volatile FW Client ID for FSL, unique for all active devices
	FWReferenceID			localFWReferenceID;				// reference to local node, may change if more than one FW interface is available
	RegEntryID				regEntryID;						// used to register driver with FireWire
	CSRNodeUniqueID			uniqueID;						// peristent 64-bit EUID for 1394
	IDHNotificationID 		readIsochNotificationID;		// read notification ID
	IDHNotificationID 		writeIsochNotificationID;		// write notification ID
	IsochComponentInstance	*instancePtr[kMaxInstances];	// clients that are attached to device
	ComponentInstance		deviceControlInstance;			// device control component instance
	IDHNotificationID		deviceControlNotificationID;	// tell me when device is added/removed so that i can adjust fwClientID
	Component				clock;							// FireWire clock component, client's get their own instance, not sure why
	UInt32 					standard; 						// device standard
	QTAtomContainer			deviceContainer;				// contains device database
	IsochPortID				isochPortID;					// contains FireWire isoch port ID for active stream
	IsochChannelID			outputIsochChannelID;			// FireWire channel ID for isoch transmit
	IsochChannelID			inputIsochChannelID;			// FireWire channel ID for isoch receive
	DVGlobalOutPtr			pGlobalDVOutData;				// global data for output DCL
	DVGlobalInPtr			pGlobalDVInData;				// global data for input DCL
	DCLProgramID			dclProgramID;					// ID of DCL program
	FWCommandObjectID		isochPortCommandObjectID;		// FireWire command object used in init/release isoch port routines
	FWCommandObjectID		startIsochPortCommandObjectID;	// FireWire command object used in start isoch port routines
	FWCommandObjectID		stopIsochPortCommandObjectID;	// FireWire command object used in stop isoch port routines
	FWCommandObjectID		asynchCommandObjectID;			// FireWire command object used in CMP
	FWAddressSpaceID		pcrID;							// address space ID of plug control register
	UInt32					pcrBuffer;						// pcr buffer space, use FWRead and FWLock to access
	UInt32					pcrIndex;						// pcr number that was allocated
	UInt32					resetStatus;					// used in bus reset processing to keep up with reset stage
	UInt32					buffer[2];						// buffer for pcr reads and locks
	UInt32					expectedPCR;					// value to check for successful update of PCR
	UInt32					cmpBandwidth;					// allocated isoch bandwidth for a stream, used in re-establishing connection after bus reset
	UInt32					cmpOverheadID;					// overheadID for a stream, used in re-establishing connection after bus reset
	UInt32					cmpChannel;						// channel for a stream, used in re-establishing connection after bus reset
	DriverRefNum			refNum;							// volatile driver refNum, unique for all active devices
	SInt16					deviceControlCount;				// number of clients using device control
	SInt16					nQueueElements;					// holds number of buffers avail				
	SInt16					readLocks;						// num clients that have device open for reads
	SInt16					writeLocks;						// num clients that have device open for writes
	SInt16					exclusiveAccess;				// does client have exclusive access?
	Boolean					complete;						// I/O complete flag for synchronous operation
	Boolean					active;							// device is currently online
	};
	
typedef struct DeviceDescription DeviceDescription, *DeviceDescriptionPtr;
	
struct ClientNotification {
	ComponentInstance 	ihc;
	IDHDeviceID			deviceID;
	IDHNotificationProc	notificationProc;
	IDHEvent			events;
	void				*userData;
	};

typedef struct ClientNotification ClientNotification;

// component globals
struct IsochComponentGlobals {
	IsochComponentInstancePtr	lastInstance;				// last instance opened
	UInt32						useCMP;						// CMP off by default, turn on by adding 'ucmp' resource 
	DeviceDescription			deviceDescription[kMaxDevicesActive];	// description of each device
	ClientNotification			clientNotification[kMaxNotifications];	// arbirtary # of notifications
	UInt32						nDevices;					// number of devices in the list
	UInt32						seed;						// keep track of device lists
	IsochComponentInstance		*localInstance;				// our local instance
	Boolean						unRegistered;				// was component unregistered?
	};
	
// per component instance variables	
struct IsochComponentInstance {
	Handle 						instanceHandle;				// instance storage handle
	ComponentInstance 			self;						// this component
	IsochComponentGlobalsPtr 	gGlobals;					// pointer to component globals
	QTAtomSpec					currentConfig;				// current device config set by client
	IDHDeviceID					deviceID;					// persistent unique ID for each device, seed value
	long						permissions;				// open permissions
	QHdrPtr 					readQueue;					// queue of read requests
	QHdrPtr 					writeQueue;					// queue of write requests
	Boolean						timeout;					// timeout for synchronous reads
	Boolean						hasDeviceControl;			// does this client have device control?
	};
	
// convenience stuff for getDeviceStandard
typedef struct AVCCTSFrameStruct {
	UInt8		cmdType_respCode;			// cmd type/response code
	UInt8		headerAddress;
	UInt8		opcode;
	UInt8		operand[5];
} AVCCTSFrameStruct, *AVCCTSFrameStructPtr;

enum {
	kAVCStatusInquiryCommand	= 0x01,
	kAVCOutputSignalModeOpcode	= 0x78,
	kAVCInputSignalModeOpcode	= 0x79,
	kAVCSignalModeSD525_60		= 0x00,
	kAVCSignalModeSDL525_60		= 0x04,
	kAVCSignalModeHD1125_60		= 0x08,
	kAVCSignalModeSD625_50		= 0x80,
	kAVCSignalModeSDL625_50		= 0x84,
	kAVCSignalModeHD1250_50		= 0x88,
	kAVCSignalModeDummyOperand	= 0xff
};

/************************************************************************************/
// Begin Dispatch Stuff

// Used by Type's .k.h to create prototypes for our routines
#define IDH_BASENAME()	IDH
#define IDH_GLOBALS()	ComponentInstance

#define IDHDV_BASENAME() IDHDV
#define IDHDV_GLOBALS()	ComponentInstance

#include "IsochronousDataHandler.k.h"
#include "IsochronousDataHandlerPriv.k.h"

// Used by Component Dispatch Helper to call our routines
#define	CALLCOMPONENT_BASENAME()		IDH
#define	CALLCOMPONENT_GLOBALS()			IsochComponentInstancePtr storage

// Other defines for Component Dispatch Helper
#define	COMPONENT_DISPATCH_FILE			"IDHDispatch.h"		/*  describes what to dispatch */

#define COMPONENT_UPP_SELECT_ROOT()		IDH					/* root for Type's UPP_PREFIX and SELECT_PREFIX */
#define COMPONENT_SUBTYPE_UPP_SELECT_ROOT()	IDHDV

#include "Components.k.h"				/* std .k.h */
#include "QuickTimeComponents.k.h"		/* Type & SubType .k.h */
#include "ComponentDispatchHelper.c"	/* make our dispatcher and cando */

// End Dispatch Stuff
/************************************************************************************/

// local prototypes
OSErr		addBuffersToQueue( DeviceDescription *deviceDescriptionPtr);
OSErr 		checkSeed( IsochComponentGlobalsPtr gGlobals, QTAtomSpec *configID);
OSErr 		closeDeviceControl( ComponentInstance ih, DeviceDescriptionPtr deviceDescriptionPtr);
OSStatus 	deviceControlCallback( IDHGenericEvent *event, void *userData);
OSErr 		doAVCTransaction( FWReferenceID ih, DVCTransactionParams* inTransaction);
OSErr 		findAtom( QTAtomSpec *atomSpec, OSType theType, QTAtom *theAtom);
OSErr 		findDeviceDescriptionforDevice( IsochComponentInstancePtr ih, UInt32 deviceID, DeviceDescription **deviceDescription);
OSErr 		getDeviceID( QTAtomSpec *configID, UInt32 *deviceID);
OSErr 		getDeviceStandard( FWReferenceID ih, UInt32 * pStandard );
OSStatus 	localCompletionRoutine(IDHGenericEvent *event, void *userData);
OSStatus 	myTimerHandler( void *p1, void *p2);
OSErr 		postEvent(IsochComponentGlobals* gGlobals, IDHDeviceID deviceID,
				IDHEvent event, void* privateData);
OSStatus	postEventSIH(void* p1, void* p2);
OSStatus 	readNotifyProc( IDHGenericEvent *event, void *userData);
OSErr 		setupAVCTransaction( FWReferenceID ih, DVCTransactionParams *pParams);
OSErr 		setupVideoAtoms( QTAtomContainer container, QTAtom isocAtom, UInt32 standard);
OSErr		setup32kAudioAtoms( QTAtomContainer container, QTAtom isocAtom, UInt32 standard);
OSErr 		setup44kAudioAtoms( QTAtomContainer container, QTAtom isocAtom, UInt32 standard);
OSErr 		setup48kAudioAtoms( QTAtomContainer container, QTAtom isocAtom, UInt32 standard);
OSStatus	writeNotifyProc( IDHGenericEvent *event, void *userData);
OSStatus	cameraNameLookup(DeviceDescriptionPtr pDeviceDescription, UInt8 *name);
OSStatus	updateCameraName(CSRNodeUniqueID *guid, UInt8 *name);
OSErr		updateDeviceList(IsochComponentInstancePtr ih, GDFDeviceEventData *pGDFData, IDHDeviceID *deviceID, Boolean doAdd);
OSStatus	handleDeviceAdded(ComponentInstance ihc, GDFDeviceEventData *pGDFData);
OSStatus	handleDeviceRemoved(ComponentInstance ihc, GDFDeviceEventData *pGDFData);
OSStatus	initIsochPort( FWClientInitIsochPortParams *pInitIsochPortParams, UInt32* outCommandAcceptance);
OSStatus	releaseIsochPort(FWClientReleaseIsochPortParams *pReleaseIsochPortParams, UInt32* outCommandAcceptance);
OSStatus	stopIsochPort( FWClientIsochPortControlParams *pIsochPortControlParams, UInt32* outCommandAcceptance);
OSStatus	startIsochPort(FWClientIsochPortControlParams *pIsochPortControlParams, UInt32* outCommandAcceptance);
void 		isochPortCommandCompletionProc(FWCommandObjectID fwCommandObjectID, OSStatus commandStatus, UInt32 completionProcData);
OSStatus 	doLocalTalkerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams);
OSStatus 	doRemoteTalkerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams);
OSStatus 	trialLocalTalkerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams);
OSStatus 	trialRemoteTalkerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams);
OSStatus 	doLocalListenerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams);
OSStatus 	doRemoteListenerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams);
OSStatus 	trialLocalListenerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams);
OSStatus 	trialRemoteListenerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams);
OSErr		cmpNewPointToPointConnection(UInt32 pcrOffset, FWClientID clientID, UInt32 channel, UInt32 speed, UInt32 overhead, DeviceDescriptionPtr pDeviceDescription);
OSErr		cmpDisposePointToPointConnection(UInt32 pcrOffset, FWClientID clientID, DeviceDescriptionPtr pDeviceDescription);
OSStatus	irmReleaseChannel(FWClientID fwClientID, UInt32 channel, DeviceDescriptionPtr pDeviceDescription);
OSStatus	irmReleaseBandwidth(FWClientID fwClientID, UInt32 fwBandwidthUnits, DeviceDescriptionPtr pDeviceDescription);
OSStatus	irmAllocateChannel(FWClientID fwClientID, UInt32 *channel, DeviceDescriptionPtr pDeviceDescription);
OSStatus	irmAllocateBandwidth(FWClientID fwClientID, UInt32 fwBandwidthUnits, DeviceDescriptionPtr pDeviceDescription);
UInt32		irmCalculateBandwidthUnits(UInt32 pcrValue);
OSStatus	cmpHandleLocalLock(FWClientAsynchRequestParamsPtr pFWClientAsynchRequestParams, UInt32 *pCommandAcceptance);
OSErr		cmpRead(UInt32 pcrOffset, FWClientID clientID, UInt32 *pcrValue, DeviceDescriptionPtr pDeviceDescription);
OSErr		cmpWrite(UInt32 pcrOffset, FWClientID clientID, UInt32 expectedPCRValue, UInt32 newPCRValue, DeviceDescriptionPtr pDeviceDescription);
OSStatus	cmpHandleBusReset(FWClientInterfaceParamsPtr pParams, UInt32 *pCommandAcceptance);
OSErr		cmpResetRead(UInt32 offsetLo, FWClientID clientID, FWCommandCompletionProcPtr completionProc, DeviceDescriptionPtr deviceDescriptionPtr);
OSErr		cmpResetWrite(UInt32 offsetLo, FWClientID clientID, FWCommandCompletionProcPtr completionProc, DeviceDescriptionPtr deviceDescriptionPtr);
void		cmpResetStageOne(DeviceDescriptionPtr pDeviceDescription);
void		cmpResetStageTwo(FWCommandObjectID theObjectID, OSStatus status, UInt32 data);
void		cmpResetStageThree(FWCommandObjectID theObjectID, OSStatus status, UInt32 data);
void		cmpResetStageFour(FWCommandObjectID theObjectID, OSStatus status, UInt32 data);
void		cmpResetStageFive(FWCommandObjectID theObjectID, OSStatus status, UInt32 data);
void		cmpResetStageSix(FWCommandObjectID theObjectID, OSStatus status, UInt32 data);
void		cmpResetStageSeven(FWCommandObjectID theObjectID, OSStatus status, UInt32 data);
void		cmpResetStageEight(FWCommandObjectID theObjectID, OSStatus status, UInt32 data);
void		cmpResetStageNine(FWCommandObjectID theObjectID, OSStatus status, UInt32 data);
void		cmpResetComplete(FWCommandObjectID theObjectID, OSStatus status, UInt32 data);
OSErr		enableRead(IsochComponentInstancePtr ih);
OSErr		enableWrite(IsochComponentInstancePtr ih);
OSErr		disableRead(IsochComponentInstancePtr ih);
OSErr		disableWrite(IsochComponentInstancePtr ih);
OSErr 		dclInitOutput(DeviceDescriptionPtr pDeviceDescription, IsochComponentGlobals* isochComponentGlobals);
DVLocalOutPtr		dclAllocatePlayBufferGroup(DVGlobalOutPtr pGlobalData);
DCLCommandPtr		dclAllocateCommand(DCLCommandPoolPtr pDCLCommandPool, UInt32 dclSize);
DCLCommandBlockPtr	dclAllocateCommandBlock(DCLCommandPoolPtr pDCLCommandPool);
DCLCommandPoolPtr	dclAllocateCommandPool(void);
void 				dclDeallocateCommandPool( DCLCommandPoolPtr pDCLCommandPool );
void				dclDeallocatePlayBufferGroup( DVLocalOutPtr	pLocalData );
void		dclDeallocateCommandBlock(DCLCommandBlockPtr pDCLCommandBlock);
void 		handleDVOutput(DCLCommandPtr pDCLCommandPtr);
void		handleDVOutputUnderrun(DCLCommandPtr pDCLCommandPtr);
void		updateDVOutputBuffers(DCLCommandPtr pDCLCommandPtr);
void		handleDVOutputUnderrunCompletion(FWCommandObjectID fwCommandObjectID, OSStatus commandStatus, UInt32 completionProcData);
OSErr		createDVPlayBufferGroupUpdateList( DVLocalOutPtr pLocalData);
OSErr		disposeDCLOutput( DVGlobalOutPtr pOutputData );
UInt32		addFWCycleTimeToFWCycleTime( UInt32 cycleTime1, UInt32 cycleTime2 );
UInt32		subtractFWCycleTimeFromFWCycleTime( UInt32 cycleTime1, UInt32 cycleTime2 );
UInt32		convertFractionalSecondsToFWCycleTime( UInt32 secondsNumerator, UInt32 secondsDenominator );
OSErr		getNextFullOutputFrame( DVIODataPtr pOData, UInt32** ppFrame );
OSErr		getNextEmptyOutputFrame( DVIODataPtr pOData, UInt32** ppFrame);
OSErr		queueNextFullOutputFrame( DVIODataPtr pOData, UInt32* pFrame );
OSErr		getNextEmptyInputFrame( DVIODataPtr pIData, UInt32** ppFrame );
OSErr 		getNextFullInputFrame( DVIODataPtr pIData, UInt32** ppFrame );
OSErr 		releaseFullInputFrame( DVIODataPtr pIData, UInt32* pFrame );
Boolean		isDVFinished( DVIODataPtr pIOData );
OSErr		getCurrentDCLFrame( DVIODataPtr pIOData, UInt32** ppFrame );
OSErr 		initDVFrameOutput(UInt32 standard, DVIODataPtr* ppIOData);
OSErr 		initDVFrameInput(UInt32 standard, DVIODataPtr* ppIOData);
OSErr 		releaseDVFrameIO( DVIODataPtr pIOData );
void		deleteDVFrameAndElement( DVFrameQElemPtr pQElem );
OSErr		dclInitInput(DeviceDescriptionPtr pDeviceDescription, IsochComponentGlobals* isochComponentGlobals);
void		storeDVPackets( DCLCommandPtr pDCLCommandPtr );
OSErr		disposeDCLInput( DVGlobalInPtr pInputData );
OSErr 		writeFrame( IsochComponentInstancePtr ih, char *tmpBuffPtr);
OSErr		getEmptyOutputFrame( IsochComponentInstancePtr ih, Ptr *tmpBuffPtr, UInt32 *size);
OSStatus	registerDevice(RegEntryID* inRegEntryID, FWDriverID* outDriverID, DeviceDescriptionPtr pDeviceDescription);
Boolean		useCMP(void);
OSStatus	handleBusReset(FWClientInterfaceParamsPtr pParams, UInt32 *pCommandAcceptance);

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Component Routines

#pragma mark -
#pragma mark ееееееееее Standard Component Calls  ееееееееее

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Reverses anything done in the open call.
// See this mark for more information on variables handling: GlobalsAndPrefs.
static pascal ComponentResult IDHClose( IsochComponentInstancePtr iGlobals, ComponentInstance self )
{
	IsochComponentGlobalsHandle	globHdl = 0L;							// Handle to our globals
	OSErr						result = noErr;
	IsochComponentGlobalsPtr	gGlobals;
	UInt32						i;
	
	RecordEventLogger( 'isoc', 'clos', 0, (unsigned long) iGlobals);

	FailMessage(self == 0);	

	globHdl = (IsochComponentGlobalsHandle) GetComponentRefcon((Component) self);	// Try and get our refCon
	if ( (globHdl == nil) || ((UInt32) globHdl == 0xFFFFFFFF) || (iGlobals == nil))	// If its all F's this is a close for register
	{																		// otherwise it is a real close
		return(noErr);
	}
	
	gGlobals = *globHdl;
	
	if( iGlobals->deviceID && iGlobals->hasDeviceControl)	// Close device control
	{
		DeviceDescription *deviceDescriptionPtr;
		
		result = findDeviceDescriptionforDevice( iGlobals, iGlobals->deviceID, &deviceDescriptionPtr);
		FailWithVal( result != noErr, Exit, result);
		
		result = closeDeviceControl( (ComponentInstance) iGlobals, deviceDescriptionPtr);
		FailMessage( result != noErr);
		 
		iGlobals->hasDeviceControl = false;
	}	
	
	if( iGlobals->permissions)
		IDHCloseDevice( (ComponentInstance) iGlobals);	// close device in case the client forgot to
	
	for( i=0; i<kMaxNotifications; ++i)	// purge all notifications for this client
	{
		if( gGlobals->clientNotification[i].ihc == (ComponentInstance) iGlobals)
		{
			RecordEventLogger( 'isoc', 'clos', 'not-', (unsigned long) iGlobals);
			
			gGlobals->clientNotification[i].deviceID = 0;
			gGlobals->clientNotification[i].ihc = 0;
		}
	}
			
	PBQueueDelete( iGlobals->readQueue);	// delete queues
	PBQueueDelete( iGlobals->writeQueue);

	if (CountComponentInstances((Component)self) <= 1)				// Is this the last close?
	{	
		int 			i;
		QueueElement	*qep;

		RecordEventLogger( 'isoc', 'clos', 0, 'last');

		for( i=0; i<kMaxDevicesActive; ++i)	// free queue elements
		{
			DeviceDescription *deviceDescriptionPtr = &gGlobals->deviceDescription[i];

			// teardown read just in case the client forgot
			if( deviceDescriptionPtr->deviceID && deviceDescriptionPtr->readIsochNotificationID)
			{
				result = IDHCancelNotification( (ComponentInstance)gGlobals->localInstance, deviceDescriptionPtr->readIsochNotificationID);
				FailMessage( result != noErr);
				
				result = IDHDisposeNotification( (ComponentInstance)gGlobals->localInstance, deviceDescriptionPtr->readIsochNotificationID);
				FailMessage( result != noErr);
				
				deviceDescriptionPtr->readIsochNotificationID = nil;

				if( deviceDescriptionPtr->readLocks > 0)
				{
					result = disableRead((IsochComponentInstancePtr) iGlobals);
					FailMessage( result != noErr);
					
					deviceDescriptionPtr->readLocks = 0;
				}
			}

			// teardown write just in case the client forgot
			if( deviceDescriptionPtr->deviceID && deviceDescriptionPtr->writeIsochNotificationID)
			{
				result = IDHCancelNotification( (ComponentInstance)gGlobals->localInstance, deviceDescriptionPtr->writeIsochNotificationID);
				FailMessage( result != noErr);
				
				result = IDHDisposeNotification( (ComponentInstance)gGlobals->localInstance, deviceDescriptionPtr->writeIsochNotificationID);
				FailMessage( result != noErr);
				
				deviceDescriptionPtr->writeIsochNotificationID = nil;

				if( deviceDescriptionPtr->writeLocks > 0)
				{
					result = disableWrite((IsochComponentInstancePtr) iGlobals);
					FailMessage( result != noErr);
					
					deviceDescriptionPtr->writeLocks = 0;
				}
			}

			// free all of the queue buffers
			while( PBDequeueFirst( gGlobals->deviceDescription[i].freeQueue, (QElemPtr *) &qep) == noErr)
			{
				if( qep)
				{
					if( qep->buffer)
						DisposePtr( qep->buffer);
						
					DisposePtr( (char *) qep);
				}
			}
			
			while( PBDequeueFirst( gGlobals->deviceDescription[i].usedQueue, (QElemPtr *) &qep) == noErr)
			{
				if( qep)
				{
					if( qep->buffer)
						DisposePtr( qep->buffer);
						
					DisposePtr( (char *) qep);
				}
			}
			
			gGlobals->deviceDescription[i].readLocks = gGlobals->deviceDescription[i].writeLocks = 0;
		}
		
		
		// Unregister all devices that have ever been seen.
		for (i=0; i < gGlobals->nDevices; ++i)
		{
			result = FWUnregisterDriver(gGlobals->deviceDescription[i].fwClientID);
			FailMessage(noErr != result);
		}

		// make sure all notifications are cancelled		
		for (i = 0; i < kMaxNotifications; ++i)
		{
			if( gGlobals->clientNotification[i].deviceID != 0 && 
				gGlobals->clientNotification[i].ihc != (ComponentInstance) gGlobals->localInstance)
			{
				gGlobals->clientNotification[i].deviceID = 0;
				log_debugstr( "All notifications weren't cancelled in last close !!!");
			}
		}
		
		if( gGlobals->unRegistered)
		{
			for( i=0; i<kMaxDevicesActive; ++i)	// free queue elements
			{
				DeviceDescription *deviceDescriptionPtr = &gGlobals->deviceDescription[i];

				result = closeDeviceControl( (ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr);
				FailMessage( result != noErr);
				
				if( deviceDescriptionPtr->deviceContainer)	// free device atoms
				{
					result = QTRemoveChildren( deviceDescriptionPtr->deviceContainer, kParentAtomIsContainer);
					FailMessage( result != noErr);
					
					result = QTDisposeAtomContainer( deviceDescriptionPtr->deviceContainer);
					FailMessage( result != noErr);
					
					deviceDescriptionPtr->deviceContainer = nil;
				}
			}
						
			for( i=0; i<kMaxDevicesActive; ++i)
			{
				PBQueueDelete( gGlobals->deviceDescription[i].freeQueue);	// delete device queues
				PBQueueDelete( gGlobals->deviceDescription[i].usedQueue);
			}
			
			if( gGlobals->localInstance)
				DisposePtr( (Ptr) gGlobals->localInstance);
			
			HUnlock((Handle)globHdl);
			DisposeHandle((Handle)globHdl);
			SetComponentRefcon((Component) self, 0xFFFFFFFF);	// component is now unused
			gGlobals = nil;
		}
	}

	if( iGlobals != nil && iGlobals->instanceHandle) // dispose instance handle
		DisposeHandle( iGlobals->instanceHandle);

Exit:
	FailMessage( result != noErr);
	
	return (result);
}

pascal ComponentResult IDHVersion( IsochComponentInstance *storage)
{
#pragma unused (storage)

	RecordEventLogger( 'isoc', 'vers', 0, 0);

	return 0x10000;
}

pascal ComponentResult IDHTarget(struct IsochComponentInstance *storage, ComponentInstance parentComponent)
{
#pragma unused (storage, parentComponent)

	RecordEventLogger( 'isoc', 'targ', 0, 0);

	return(noErr);
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Opens the component instance.
// See this mark for more information on variables handling: GlobalsAndPrefs.
static pascal ComponentResult IDHOpen( struct IsochComponentInstance *unused1, ComponentInstance self )
{
#pragma unused (unused1)

	IsochComponentInstancePtr		instancePtr = nil;		// Pointer to instance globals
	Handle							instanceHandle = nil;	// Handle to instance globals
	ComponentResult					result = noErr;			// Result return by this routine
	IsochComponentGlobalsHandle		refConHdl = nil;
	IsochComponentGlobalsPtr		gGlobals = nil;
	
	// Check the ComponentRefcon to see if this 'Open' request is part of the
	// registering process (Open, Register, Close) or a true 'Open'.
	// By convention in FWD, the ComponentRefcon is used to convey Registration
	// information and to point to a structure of items that are shared by component
	// instances as shown below:
	//
	//			NULL
	//			The component has NOT been successfully registered.
	//		
	//			0xFFFFFFFF
	//			The component has been successfully registered, but NO shared data
	//			has been allocated or initialized.
	//
	//			Other Value
	//			Any other value means that the component has been successfully
	//			registered.  Additionally, it points to the data that is shared
	//			between ComponentInstances.

//еее	RecordEventLogger( 'isoc', 'open', 0, 0);

	SetComponentInstanceStorage(self, (Handle) 0);				// save instance ptr in component

	refConHdl = (IsochComponentGlobalsHandle) GetComponentRefcon((Component) self);
	if ( refConHdl == nil )					// Is this a register call?
	{
		RecordEventLogger( 'isoc', 'open', 'reg ', 0);
		gCurInstance = self;						// Save for register
		return(noErr);								// Just return lt register happen
	}

	//======================================================================
	// Start allocating instance storage
	
	ReserveMem( sizeof(IsochComponentInstance) );
	instanceHandle = NewHandleClear( sizeof(IsochComponentInstance) );	// allocate instance storage
	FailWithAction( instanceHandle == nil, result = MemError(), BadExit);

	HLock((Handle) instanceHandle);
	instancePtr = (IsochComponentInstancePtr) *instanceHandle;
	instancePtr->instanceHandle = instanceHandle;
	instancePtr->self = self;
	SetComponentInstanceStorage(self, (Handle) instancePtr);				// save instance ptr in component

	RecordEventLogger( 'isoc', 'open', 0, (unsigned long) instancePtr);

Exit:	
	instancePtr->gGlobals		= *refConHdl;
	gGlobals 					= *refConHdl;
	gGlobals->lastInstance		= instancePtr;
		
	// allocate queues for client read and write requests
	result = PBQueueCreate( &instancePtr->readQueue);
	FailWithVal( result != noErr, BadExit, result);	
	
	result = PBQueueInit( instancePtr->readQueue);
	FailWithVal( result != noErr, BadExit, result);	
	
	result = PBQueueCreate( &instancePtr->writeQueue);
	FailWithVal( result != noErr, BadExit, result);	
	
	result = PBQueueInit( instancePtr->writeQueue);
	FailWithVal( result != noErr, BadExit, result);	

	FailMessage( result != noErr);
	
	if (1 == CountComponentInstances((Component)self))		// Is this the first Open?
	{
		// Register all devices that are currently online.
		UInt32 devIndex;
		OSStatus status;
		RecordEventLogger('isoc', '1st ', 'open', gGlobals->nDevices);

		for (devIndex = 0; devIndex < gGlobals->nDevices; ++devIndex)
		{
			status = registerDevice(
					&gGlobals->deviceDescription[devIndex].regEntryID,
					&gGlobals->deviceDescription[devIndex].fwClientID,
					&gGlobals->deviceDescription[devIndex]);
			
			FailMessage(noErr != status);
		}
	}
	
	return result;
	
BadExit:

	// chunk component stuff if last instance
	if (CountComponentInstances((Component)self) == 1 && gGlobals != nil) 
	{
		int i;

		for( i=0; i<kMaxDevicesActive; ++i)
		{
			PBQueueDelete( gGlobals->deviceDescription[i].freeQueue);	// release queues
			PBQueueDelete( gGlobals->deviceDescription[i].usedQueue);	// release queues
		}
		
		DisposeHandle( (Handle) refConHdl);
		refConHdl = nil;
		gGlobals = nil;

		SetComponentRefcon((Component) self, (SInt32) 0xFFFFFFFF);		
	}
	
	if( instanceHandle != nil)
	{
		DisposeHandle( instanceHandle);
		instanceHandle = nil;
	}
	
	FailMessage( result != noErr);
	
	return result;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Registers the component. Returns non-zero result if component should not 
// be registered. This routine is called by the IDHOpen routine 
// after the instance globals are allocated, but before the component globals 
// are instantiated. This routine should not call any other functions that 
// require component globals or preferences.
// See this mark for more information on variables handling: GlobalsAndPrefs.
static pascal ComponentResult IDHRegister( IsochComponentInstancePtr globals )
{
#pragma unused( globals)

	ComponentResult 			result = false;		// Assume success
	int 						i;
	IsochComponentGlobalsHandle refConHdl;
	IsochComponentGlobals		*gGlobals;
	IsochComponentInstance		*localInstance;
					
	RecordEventLogger( 'isoc', 'reg ', 0, 0);
	
	//=========================================================================
	// Allocate globals for all component instances
	
	ReserveMem(sizeof(IsochComponentGlobals));
	refConHdl = (IsochComponentGlobalsHandle) NewHandleSysClear(sizeof(IsochComponentGlobals));	// Try and allocate refCon data
	FailWithAction( refConHdl == nil, result = MemError(), Exit);

	HLock( (Handle) refConHdl);
	gGlobals = *refConHdl;
	
	SetComponentRefcon((Component) gCurInstance, (long) refConHdl);
	
	for( i=0; i<kMaxDevicesActive; ++i)	// initialize device decription structs
	{
		int j;
		
		// create free and used queue for each device
		result = PBQueueCreate( &gGlobals->deviceDescription[i].freeQueue);
		FailWithVal( result != noErr, Exit, result);	
		
		result = PBQueueInit( gGlobals->deviceDescription[i].freeQueue);
		FailWithVal( result != noErr, Exit, result);	
					
		result = PBQueueCreate( &gGlobals->deviceDescription[i].usedQueue);
		FailWithVal( result != noErr, Exit, result);	
		
		result = PBQueueInit( gGlobals->deviceDescription[i].usedQueue);
		FailWithVal( result != noErr, Exit, result);	

		// initialize other variables
		gGlobals->deviceDescription[i].nQueueElements 				= 0;
		gGlobals->deviceDescription[i].deviceID		 				= nil;
		gGlobals->deviceDescription[i].readLocks 					= 0;
		gGlobals->deviceDescription[i].writeLocks 					= 0;
		gGlobals->deviceDescription[i].exclusiveAccess 				= 0;
		gGlobals->deviceDescription[i].readIsochNotificationID 		= nil;
		gGlobals->deviceDescription[i].writeIsochNotificationID 	= nil;
		
		for( j=0; j<kMaxInstances; ++j)
			gGlobals->deviceDescription[i].instancePtr[j] = nil;	
	}

	// see if CMP should be turned on
	gGlobals->useCMP = useCMP();
	
	localInstance = (IsochComponentInstance *) NewPtrSysClear( sizeof( IsochComponentInstance));
	FailWithAction( localInstance == NULL, result = MemError(), Exit);
		
	localInstance->self 	= (ComponentInstance) localInstance;	
	localInstance->gGlobals	= gGlobals;	// pointer to component globals
		
	gGlobals->localInstance = localInstance;			

	gGlobals->seed 		= 1;
	gGlobals->nDevices	= 0;

Exit:
	return result;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// The component is being unregistered. Mark the globals to be disposed.
// See this mark for more information on variables handling: GlobalsAndPrefs.
static pascal ComponentResult IDHUnregister( IsochComponentInstancePtr iGlobals )
{
	IsochComponentGlobalsPtr	gGlobals;

	RecordEventLogger( 'isoc', 'ureg', 0, 0);

	FailIf(iGlobals == nil, Exit);
	
	gGlobals = iGlobals->gGlobals;
	FailIf(gGlobals == nil, Exit);
	
	gGlobals->unRegistered = true;

Exit:
	return noErr;
}

#pragma mark -
#pragma mark ееееееееее IDH Component Calls  ееееееееее

pascal ComponentResult	IDHCancelNotification( ComponentInstance ihc, IDHNotificationID	notificationID)
{
	OSErr 						result = noErr;
	ClientNotification*			clientNotification = (ClientNotification*)notificationID;
	
	RecordEventLogger( 'isoc', 'canc', 'el  ', 'noti');

	FailWithAction( clientNotification == nil, result = paramErr, Exit);
	
	clientNotification->events = 0;
	
Exit:	
	return result;	
}											

pascal ComponentResult IDHCancelPendingIO( ComponentInstance ihc, IDHParameterBlock *pb)
{
	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
	OSErr 						result = noErr;
	QHdrPtr						queue = nil;
	
	RecordEventLogger( 'isoc', 'canc', 'el  ', 'pend');
	
	FailWithAction( pb == nil, result = paramErr, Exit);

	if( ih->permissions & kIDHOpenForReadTransactions)
		queue = ih->readQueue;
	
	if( ih->permissions & kIDHOpenForWriteTransactions)
		queue = ih->writeQueue;	

	if( queue)
	{
		result = PBDequeue( (QElem *) pb, queue);
//		FailWithVal( result != noErr, Exit, result);
		if( result != noErr)
			goto Exit;
	}

Exit:	
	return result;	
	
}

pascal ComponentResult	IDHCloseDevice(	ComponentInstance ihc)
{
	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
	OSErr 						result = noErr;
	IsochComponentGlobalsPtr	gGlobals = ih->gGlobals;
	DeviceDescription			*deviceDescriptionPtr = nil;
	short						i;
	IDHParameterBlock 			*IORequest;
	
	RecordEventLogger( 'isoc', 'clos', ' dev', 0);

	FailWithAction( ih->deviceID == nil, result = kIDHErrDeviceNotConfigured, Exit);
	
	if( ih->permissions == 0)	// client has no open devices?
		goto Exit;

	// flush IO queues
	while( PBDequeueFirst( ih->readQueue, (QElemPtr *) &IORequest) == noErr) ;
	while( PBDequeueFirst( ih->writeQueue, (QElemPtr *) &IORequest) == noErr) ;	

	result = findDeviceDescriptionforDevice( ih, ih->deviceID, &deviceDescriptionPtr);	// find the device
	FailWithVal( result != noErr, Exit, result);
	
	for( i=0; i<kMaxInstances; ++i) // remove instance from device list
	{
		if( deviceDescriptionPtr->instancePtr[i] == ih)
		{
			deviceDescriptionPtr->instancePtr[i] = nil;
		}
	}
	
	FailWithAction( deviceDescriptionPtr->deviceID == nil, result = kIDHErrDeviceNotOpened, Exit);

	if( ih->permissions & kIDHOpenForReadTransactions)	// tear down read
	{
		if( --deviceDescriptionPtr->readLocks <= 0)	// is device completely freed by clients?
		{
			if( deviceDescriptionPtr->readIsochNotificationID)
			{
				result = IDHCancelNotification( (ComponentInstance) gGlobals->localInstance, 
							deviceDescriptionPtr->readIsochNotificationID);
				FailMessage( result != noErr);
				
				result = IDHDisposeNotification( (ComponentInstance) gGlobals->localInstance, 
							deviceDescriptionPtr->readIsochNotificationID);
				FailMessage( result != noErr);
				
				deviceDescriptionPtr->readIsochNotificationID = nil;
			}

			result = disableRead(ih);
			FailMessage( result != noErr);

			deviceDescriptionPtr->exclusiveAccess = false;
		}
	}
	
	if( ih->permissions & kIDHOpenForWriteTransactions) // tear down write
	{
		if( --deviceDescriptionPtr->writeLocks <= 0)	// is device completely freed by clients?
		{
			if( deviceDescriptionPtr->writeIsochNotificationID)
			{
				result = IDHCancelNotification( (ComponentInstance) gGlobals->localInstance, 
							deviceDescriptionPtr->writeIsochNotificationID);
				FailMessage( result != noErr);
				
				result = IDHDisposeNotification( (ComponentInstance) gGlobals->localInstance, 
							deviceDescriptionPtr->writeIsochNotificationID);
				FailMessage( result != noErr);
				
				deviceDescriptionPtr->writeIsochNotificationID = nil;
			}

			result = disableWrite(ih);
			FailMessage( result != noErr);

			deviceDescriptionPtr->exclusiveAccess = false;
		}
	}
		
	result = postEvent( ih->gGlobals, ih->deviceID, 
		(ih->permissions & kIDHOpenForReadTransactions)?kIDHEventReadDisabled:kIDHEventWriteDisabled, nil);
	FailWithVal( result != noErr, Exit, result);
			
Exit:
	ih->permissions = 0;	// make sure device is closed		
	
	return result;
}

pascal ComponentResult	IDHDisposeNotification(
		ComponentInstance ihc,
		IDHNotificationID notificationID)
{
	OSErr 					result = noErr;
	ClientNotification*		clientNotification = (ClientNotification*)notificationID;

	RecordEventLogger( 'isoc', 'disp', 'ose ', 'noti');

	FailWithAction( clientNotification == nil, result = paramErr, Exit);

	clientNotification->deviceID = 0;
	
Exit:	
	return result;	
}											

pascal ComponentResult	IDHGetDeviceClock(	ComponentInstance 	ihc,
											Component			*clock )
{
	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
	DeviceDescription			*deviceDescriptionPtr;
	OSErr 						result = noErr;

	RecordEventLogger( 'isoc', 'get ', 'clok', 0);

	FailWithAction( clock == nil, result = paramErr, Exit);

	FailWithAction( ih->deviceID == nil, result = kIDHErrDeviceNotConfigured, Exit);

	result = findDeviceDescriptionforDevice( ih, ih->deviceID, &deviceDescriptionPtr);
	FailWithVal( result != noErr, Exit, result);

//	FailWithAction( deviceDescriptionPtr->active == false, result = kIDHErrDeviceDisconnected, Exit);
	
	*clock = deviceDescriptionPtr->clock;
	
Exit:
	return result;
}											

//еее USER BETTER KEEP HIS DEVICE LIST AROUND IF THIS IS TO MEAN ANYTHING
pascal ComponentResult	IDHGetDeviceConfiguration( 	ComponentInstance 	ihc,
													QTAtomSpec			*configID )
{
	IsochComponentInstancePtr	ih = (IsochComponentInstancePtr) ihc;
	OSErr 						result = noErr;

	RecordEventLogger( 'isoc', 'get ', 'dev ', 'conf');

	FailWithAction( configID == nil, result = paramErr, Exit);
	FailWithAction( ih->currentConfig.container == nil || ih->currentConfig.atom == nil,
		result = kIDHErrDeviceNotConfigured, Exit);

	*configID = ih->currentConfig;
	
Exit:
	return result;
}								

pascal ComponentResult IDHGetDeviceControl(ComponentInstance ihc, ComponentInstance *deviceControl)
{
	ComponentResult				result = noErr;
	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
	DeviceDescription			*deviceDescriptionPtr;

	RecordEventLogger( 'isoc', 'get ', 'dev ', 'ctrl');

	if ( deviceControl == nil )
		return(paramErr);
	
	FailWithAction( ih->deviceID == nil, result = kIDHErrDeviceNotConfigured, Exit);

	result = findDeviceDescriptionforDevice( ih, ih->deviceID, &deviceDescriptionPtr);
	FailWithVal( result != noErr, Exit, result);

	FailWithAction( deviceDescriptionPtr->active == false, result = kIDHErrDeviceDisconnected, Exit);

	*deviceControl = deviceDescriptionPtr->deviceControlInstance;
		
	FailMessageVal( *deviceControl == nil, (long) ih); 

Exit:
	return(result);
}

//еее USER MUST FREE ATOM LIST WHEN FINISHED
pascal ComponentResult	IDHGetDeviceList(	ComponentInstance 	ihc,
											QTAtomContainer		*deviceList )
											
{
	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
	IsochComponentGlobalsPtr	gGlobals = ih->gGlobals;
	OSErr 						result = noErr;
	QTAtomContainer				container = nil;
	short						devIndex;
	UInt32						version;
	
	RecordEventLogger( 'isoc', 'get ', 'dev ', 'list');

	FailWithAction( deviceList == nil, result = paramErr, Exit);

	// create device atom list now	
	result = QTNewAtomContainer( &container);
	FailWithVal( result != noErr, Exit, result);	
				
	// save seed
	result = QTInsertChild( container, kParentAtomIsContainer, kIDHSeedAtomType, 0, 0, 
				sizeof( gGlobals->seed), &gGlobals->seed, nil);
	FailWithVal( result != noErr, Exit, result);			

	version = (DVVersion << 24) | (DVRevision << 16) | DVBuildNumber;
	
	// save isoch version
	result = QTInsertChild( container, kParentAtomIsContainer, kIDHIsochVersionAtomType, 0, 0, 
				sizeof( UInt32), &version, nil);
	FailWithVal( result != noErr, Exit, result);			

	// save useCMP value
	result = QTInsertChild( container, kParentAtomIsContainer, kIDHUseCMPAtomType, 0, 0, 
				sizeof( UInt32), &gGlobals->useCMP, nil);
	FailWithVal( result != noErr, Exit, result);			

	// save each active device
	for( devIndex=0; devIndex<kMaxDevicesActive; ++devIndex)
	{
		if( gGlobals->deviceDescription[devIndex].deviceID != 0 && gGlobals->deviceDescription[devIndex].active)
		{
			result = QTInsertChildren( container, kParentAtomIsContainer, gGlobals->deviceDescription[devIndex].deviceContainer);
			FailWithVal( result != noErr, Exit, result);	
		}
	}
	
	*deviceList = container;

Exit:
	if( result != noErr && container)
	{
		QTRemoveChildren( container, kParentAtomIsContainer);
		QTDisposeAtomContainer( container);
		container = nil;	
	}
	
	return result;
}											

pascal ComponentResult	IDHGetDeviceStatus(	ComponentInstance 	ihc,
											const QTAtomSpec	*devSpec,
											IDHDeviceStatus		*status )
{
	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
	IsochComponentGlobalsPtr	gGlobals = ih->gGlobals;
	OSErr 						result = noErr;
	IDHDeviceID					deviceID = nil;
	QTAtom						deviceInfoAtom, deviceAtom;
	QTAtomSpec					volatileAtomSpec;
	long						size;
	DeviceDescription			*deviceDescriptionPtr;
	
	RecordEventLogger( 'isoc', 'get ', 'stat', 0);
	
	FailWithAction( devSpec == nil, result = paramErr, Exit);
	FailWithAction( status == nil, result = paramErr, Exit);

	volatileAtomSpec = *devSpec;
	
	result = checkSeed( ih->gGlobals, &volatileAtomSpec);
	if( result != noErr)
		goto Exit;
	
	result = getDeviceID( &volatileAtomSpec, &deviceID);
	FailWithVal( result != noErr, Exit, result);

	result = findDeviceDescriptionforDevice( ih, deviceID, &deviceDescriptionPtr);	// find description for this device
	FailWithVal( result != noErr, Exit, result);

	deviceAtom = QTFindChildByIndex( deviceDescriptionPtr->deviceContainer, kParentAtomIsContainer, kIDHDeviceAtomType, 1, nil);
	FailWithAction( deviceAtom == nil, result = kIDHErrDeviceList, Exit);

	// find device status for this device
	deviceInfoAtom = QTFindChildByIndex( deviceDescriptionPtr->deviceContainer, deviceAtom, kDVDeviceInfo, 1, nil);
	FailWithAction( deviceInfoAtom == nil, result = kIDHErrDeviceList, Exit);
	
	QTLockContainer( deviceDescriptionPtr->deviceContainer);

	// get the value of the mediaType atom
	QTCopyAtomDataToPtr( deviceDescriptionPtr->deviceContainer, deviceInfoAtom, true, sizeof( IDHDeviceStatus), 
		status, &size);
			
	QTUnlockContainer( deviceDescriptionPtr->deviceContainer);

	status->version = 				100;
	status->physicallyConnected =	true;
	status->readEnabled = 			deviceDescriptionPtr->readLocks;
	status->writeEnabled = 			deviceDescriptionPtr->writeLocks;
	status->exclusiveAccess = 		deviceDescriptionPtr->exclusiveAccess;
	status->currentBandwidth = 		0;
	status->currentChannel = 		0;
	
	//еее need to make this work with camera tracking
	status->deviceActive = 			deviceDescriptionPtr->active; 
	status->inputStandard =			deviceDescriptionPtr->standard;
// JKL *** what to to with this? does this mean deviceID, cameraFWClientID, or localNodeFWClientID
// Think this is for clock to set the localFWReferenceID
	status->localNodeID	= 			(PsuedoID) deviceDescriptionPtr->localFWReferenceID;
	
	result = QTSetAtomData( deviceDescriptionPtr->deviceContainer, deviceInfoAtom, sizeof( IDHDeviceStatus), status);
	FailWithVal( result != noErr, Exit, result);
	
Exit:
	return result;
}											

pascal ComponentResult	IDHNewNotification(	ComponentInstance 	ihc,
											IDHDeviceID			deviceID,
											IDHNotificationProc	notificationProc,
											void				*userData,
											IDHNotificationID 	*notificationID)
{
	IsochComponentGlobalsPtr 	gGlobals = ((IsochComponentInstancePtr)ihc)->gGlobals;
	UInt32						i;
	Boolean						addedClient = false;
	OSErr 						result = noErr;
	
	RecordEventLogger( 'isoc', 'new ', 'noti', 'fy  ');

	FailWithAction( notificationProc == nil, result = paramErr, Exit);
	FailWithAction( notificationID == nil, result = paramErr, Exit);

	i = 0;
	while (i < kMaxNotifications)
	{
		if (0 == gGlobals->clientNotification[i].deviceID)
		{
			gGlobals->clientNotification[i].ihc = ihc;
			gGlobals->clientNotification[i].deviceID = deviceID;
			gGlobals->clientNotification[i].notificationProc = notificationProc;
			gGlobals->clientNotification[i].events = 0;
			gGlobals->clientNotification[i].userData = userData;
			*notificationID = (UInt32)&gGlobals->clientNotification[i];
			addedClient = true;
			break;
		}
		
		++i;
	}
	
	if (!addedClient)				// List is full. Unable to add addtional clients
		result = paramErr;
	
Exit:
	return result;	
}											

pascal ComponentResult	IDHNotifyMeWhen(	ComponentInstance 	ihc,
											IDHNotificationID	notificationID,
											IDHEvent			events )
{
	OSErr 						result = noErr;
	ClientNotification*			clientNotification = (ClientNotification*)notificationID;
	
	RecordEventLogger( 'isoc', 'noti', 'when', events);

	FailWithAction( clientNotification == nil, result = paramErr, Exit);

	clientNotification->events = events;
	
Exit:	
	return result;
}											

pascal ComponentResult	IDHOpenDevice( ComponentInstance ihc, UInt32 permissions)
{
	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
	IsochComponentGlobalsPtr	gGlobals = ih->gGlobals;
	OSErr 						result = noErr;
	short						i;
	DeviceDescription			*deviceDescriptionPtr = nil;
	Component					devcComp = 0;
	ComponentInstance			devc = 0;

	RecordEventLogger( 'isoc', 'open', ' dev', 0);

	FailWithAction( permissions == 0, result = paramErr, Exit);

	FailWithAction( ih->deviceID == nil, result = kIDHErrDeviceNotConfigured, Exit);

	result = findDeviceDescriptionforDevice( ih, ih->deviceID, &deviceDescriptionPtr);
	FailWithVal( result != noErr, Exit, result);
	
	FailWithAction( deviceDescriptionPtr->active == false, result = kIDHErrDeviceDisconnected, Exit);

	// trying to reopen for read or write?
	if( ih->permissions & kIDHOpenForReadTransactions && permissions & kIDHOpenForReadTransactions)
		goto Exit;	// don't do anything
	
	if( ih->permissions & kIDHOpenForWriteTransactions && permissions & kIDHOpenForWriteTransactions)
		goto Exit;	// don't do anything
	
	// check for switching directions before opening
	FailWithAction( ih->permissions & kIDHOpenForReadTransactions && permissions & kIDHOpenForWriteTransactions,
		result = kIDHErrDeviceInUse, Exit); //еее
	
	FailWithAction( ih->permissions & kIDHOpenForWriteTransactions && permissions & kIDHOpenForReadTransactions,
		result = kIDHErrDeviceInUse, Exit); //еее

	// make sure device wasn't already opened for exclusive access
	FailWithAction( deviceDescriptionPtr->exclusiveAccess == true, result = kIDHErrDeviceInUse, Exit);
	
	if( permissions & kIDHOpenForReadTransactions)	// add buffers to queue for read devices
	{
		result = addBuffersToQueue( deviceDescriptionPtr);
		FailWithVal( result != noErr, Exit, result);
	}
	
	// if user is opening for read, make sure device isn't already opened for writes			
	if( permissions & kIDHOpenForReadTransactions)
	{
		FailWithAction( deviceDescriptionPtr->writeLocks, result = kIDHErrDeviceInUse, Exit);

		if( deviceDescriptionPtr->readLocks == 0)
		{
			result = enableRead(ih);
			FailWithVal( result != noErr, Exit, result);	
		}
			
		++deviceDescriptionPtr->readLocks;	// keep track of read count
	}
			
	// if user is opening for write, make sure device isn't already opened
	if( permissions & kIDHOpenForWriteTransactions)
	{
		FailWithAction( deviceDescriptionPtr->readLocks || deviceDescriptionPtr->writeLocks, result = kIDHErrDeviceInUse, Exit);

		result = enableWrite(ih);
		FailWithVal( result != noErr, Exit, result);

		deviceDescriptionPtr->writeLocks = 1;	// keep track of write count
	}
			
	if( permissions & kIDHOpenWithExclusiveAccess)
		deviceDescriptionPtr->exclusiveAccess = true;
		
	ih->permissions = permissions;	// save the permissions
	
	// see if this client has already been registered for this device
	for( i=0; i<kMaxInstances; ++i)
		if( deviceDescriptionPtr->instancePtr[i] == ih)
			break;
			
	if( i >= kMaxInstances)	// client not registered?
	{
		for( i=0; i<kMaxInstances; ++i)
		{
			if( deviceDescriptionPtr->instancePtr[i] == nil) // find an empty slot, and add client
			{
				deviceDescriptionPtr->instancePtr[i] = ih;
				break;
			}
		}

		FailWithAction( i >= kMaxInstances, result = kIDHErrInvalidIndex, Exit);	// no slots available
	}
	
	result = postEvent( ih->gGlobals, ih->deviceID, 
		(permissions & kIDHOpenForReadTransactions)?kIDHEventReadEnabled:kIDHEventWriteEnabled, nil);
	FailWithVal( result != noErr, Exit, result);

Exit:	
	return result;
}										

// can be called synchronously at task level only
pascal ComponentResult	IDHRead( ComponentInstance ihc, IDHParameterBlock *pb)
{
	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
	OSErr 						result = noErr;
	DeviceDescription			*deviceDescriptionPtr;
	Boolean						synchronous = false;
	IsochComponentGlobalsPtr	gGlobals = ih->gGlobals;
	
	RecordEventLogger( 'isoc', 'read', (unsigned long) ih->readQueue, (unsigned long) pb);

	FailWithAction( pb == nil, result = paramErr, Exit);

	// can't do sync reads at non-task level
	FailWithAction( pb->completionProc == nil && CurrentExecutionLevel() != kTaskLevel, result = paramErr, Exit);	

	FailWithAction( ih->deviceID == nil, result = kIDHErrDeviceNotConfigured, Exit);
		
	result = findDeviceDescriptionforDevice( ih, ih->deviceID, &deviceDescriptionPtr);
	FailWithVal( result != noErr, Exit, result);

	FailWithAction( deviceDescriptionPtr->active == false, result = kIDHErrDeviceDisconnected, Exit);

	if( deviceDescriptionPtr->readIsochNotificationID == nil)
	{
		result = IDHNewNotification( (ComponentInstance) gGlobals->localInstance, ih->deviceID, readNotifyProc,
				(void *) ih->gGlobals, &deviceDescriptionPtr->readIsochNotificationID);
		FailWithVal( result != noErr, Exit, result);		
	}
	
	if ( deviceDescriptionPtr->readIsochNotificationID )
	{
		result = IDHNotifyMeWhen( (ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr->readIsochNotificationID,
				kIDHPrivateEventReadComplete);
		FailWithVal( result != noErr, Exit, result);		
	}
	
	if( pb->completionProc == nil) // synchronous read
	{
		synchronous = true;
		
		pb->completionProc = localCompletionRoutine;
		pb->refCon 			= deviceDescriptionPtr;
		
		deviceDescriptionPtr->complete = false;
	}
	
	PBEnqueueLast( (QElemPtr) pb, ih->readQueue);	// queue the read
	
	if( synchronous) // synchronous read
	{
		AbsoluteTime 	expirationTime;
		TimerID			theTimer;
		
		ih->timeout = false;
		
		// expiration is current time + 1/15 second (2 frame times)
		expirationTime = AddDurationToAbsolute( kTimeoutDuration, UpTime());
		
		result = SetInterruptTimer( &expirationTime, myTimerHandler, ih, &theTimer);
		FailWithVal( result != noErr, Exit, result);		

		while( deviceDescriptionPtr->complete == false && ih->timeout == false)	// loop until complete or timeout
			;
		
		expirationTime = DurationToAbsolute( 0);
		CancelTimer( theTimer, &expirationTime);

		pb->completionProc	= nil;
		
		FailWithAction( ih->timeout == true, result = kIDHErrDeviceTimeout, Exit); //еее correct error code
	}
	
Exit:

	return result;	
}

pascal ComponentResult	IDHReleaseBuffer( ComponentInstance ihc, IDHParameterBlock *pb)
{
	OSErr 						result = noErr;
	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
	DeviceDescription			*deviceDescriptionPtr;
	QueueElement				*qep;

	RecordEventLogger( 'isoc', 'rele', 'ase ', 'buff');

	FailWithAction( pb == nil, result = paramErr, Exit);

	FailWithAction( ih->deviceID == nil, result = kIDHErrDeviceNotConfigured, Exit);	

	result = findDeviceDescriptionforDevice( ih, ih->deviceID, &deviceDescriptionPtr);
	FailWithVal( result != noErr, Exit, result);
	
	qep = (QueueElement *) deviceDescriptionPtr->usedQueue->qHead;
	while( qep)	// find this buffer in the list of used buffers
	{
		if( qep->buffer == pb->buffer)	// is this our buffer?
		{
			if( --qep->locks <= 0)	// are all of the locks gone?
			{
				qep->locks = 0;

				// remove from used queue
				PBDequeue( (QElemPtr) qep, deviceDescriptionPtr->usedQueue);

				// enqueue this list in the free queue
				PBEnqueueLast( (QElemPtr) qep, deviceDescriptionPtr->freeQueue);	
			}
							
			break;
		}
		
		// check next element in the used queue
		qep = (QueueElement *) qep->qLink;
	}
	
Exit:		
	return result;
}

pascal ComponentResult	IDHSetDeviceConfiguration( 	ComponentInstance 	ihc,
													const QTAtomSpec	*configID)
{
	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
	OSErr 						result = noErr;
	IsochComponentGlobalsPtr	gGlobals = ih->gGlobals;
	QTAtomSpec					volatileAtomSpec;
	IDHDeviceID					previousDeviceID;
	DeviceDescription			*deviceDescriptionPtr;
	
	RecordEventLogger( 'isoc', 'set ', 'dev ', 'conf');

	FailWithAction( configID == nil, result = paramErr, Exit);

	FailWithAction( configID->container == nil, result = paramErr, Exit);
	
	// if the client is setting to the same config, then we are ok
	if( configID->container == ih->currentConfig.container &&
		configID->atom		== ih->currentConfig.atom)
		goto Exit;
		
	// device already in use, please close device first
	FailWithAction( ih->permissions != 0, result = kIDHErrDeviceInUse, Exit); 

	previousDeviceID = ih->deviceID;

	volatileAtomSpec = *configID;
	result = getDeviceID( &volatileAtomSpec, &ih->deviceID);
	FailWithVal( result != noErr, Exit, result);

	result = checkSeed( ih->gGlobals, &volatileAtomSpec);
	FailWithVal( result != noErr, Exit, result);
	
	ih->currentConfig = *configID;
		
	result = findDeviceDescriptionforDevice( ih, ih->deviceID, &deviceDescriptionPtr);
	FailWithVal( result != noErr, Exit, result);
		
	FailWithAction( deviceDescriptionPtr->active == false, result = kIDHErrDeviceDisconnected, Exit);

	// if this client already has device control, free the previous connection
	if( previousDeviceID && ih->hasDeviceControl)
	{
		DeviceDescription *deviceDescriptionPtr2;
		
		result = findDeviceDescriptionforDevice( ih, previousDeviceID, &deviceDescriptionPtr2);
		FailWithVal( result != noErr, Exit, result);
		
		result = closeDeviceControl( ihc, deviceDescriptionPtr2);
		FailMessage( result != noErr);

		ih->hasDeviceControl = false;
	}
	
	// =======================================================================
	// All should be good here from a device stand point, now open the device
	// control component.  If the devc is not nil either we didn't release it
	// from close or the same client is calling open again.
	
	if ( deviceDescriptionPtr->deviceControlInstance == nil)
	{
		ComponentDescription	devcDesc;
		Component				devcComp;
		ComponentInstance		devc;
		
		devcDesc.componentType 			= kDeviceControlComponentType;
		devcDesc.componentSubType 		= kDeviceControlSubtypeFWDV;
		devcDesc.componentManufacturer 	= 0;
		devcDesc.componentFlags 		= 0L;
		devcDesc.componentFlagsMask 	= 0L;

		devcComp = FindNextComponent( devcComp, &devcDesc);
		FailMessage( devcComp == nil);
		
		if ( devcComp )
		{
			result = OpenAComponent(devcComp, &devc);
			FailWithVal( result != noErr, Exit, result);
			
			result = DeviceControlSetDeviceConnectionID(devc, (DeviceConnectionID) deviceDescriptionPtr->fwClientID); //еее deviceID);
			FailWithVal( result != noErr, Exit, result);
				
			result = DeviceControlEnableAVCTransactions(devc);
			FailWithVal( result != noErr, Exit, result);

			deviceDescriptionPtr->deviceControlInstance = devc;
			
			// now sign up for callback in case device is added or removed and we need to update connectionID
			if( deviceDescriptionPtr->deviceControlNotificationID == nil)
			{
				result = IDHNewNotification( (ComponentInstance) gGlobals->localInstance, ih->deviceID, deviceControlCallback, deviceDescriptionPtr, 
							&deviceDescriptionPtr->deviceControlNotificationID);
				FailWithVal( result != noErr, Exit, result);
			}
			
			if ( deviceDescriptionPtr->deviceControlNotificationID )
			{
				result = IDHNotifyMeWhen( (ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr->deviceControlNotificationID,
							kIDHEventDeviceAdded | kIDHEventDeviceRemoved);
				FailWithVal( result != noErr, Exit, result);		
			}
		}
	}
	
	if( ih->hasDeviceControl == false && deviceDescriptionPtr->deviceControlInstance != nil)
	{
		ih->hasDeviceControl = true;
		++deviceDescriptionPtr->deviceControlCount;		
	}
		
Exit:
	return result;
}

pascal ComponentResult	IDHUpdateDeviceList(	ComponentInstance 	ihc,
												QTAtomContainer		*deviceList )
											
{
	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
	IsochComponentGlobalsPtr	gGlobals = ih->gGlobals;
	OSErr 						result = noErr;
	QTAtomContainer				container = nil;
	short						nDVDevices, i;
	QTAtomSpec					atomSpec;
		
	RecordEventLogger( 'isoc', 'updt', 'dev ', 'list');

	FailWithAction( deviceList == nil, result = paramErr, Exit);

	atomSpec.container = *deviceList;
	
	result = checkSeed( gGlobals, &atomSpec);	// make sure the container is current
	if( result != noErr)
		goto Exit;
		
	// check for useCMP value changing
	if (result == noErr)
	{
		QTAtom				useCMPAtom;
		UInt32				useCMPValue;
		
		useCMPAtom = QTFindChildByIndex(*deviceList, kParentAtomIsContainer, kIDHUseCMPAtomType, 1, nil);
		if (useCMPAtom)
		{
			QTLockContainer(*deviceList);
			result = QTCopyAtomDataToPtr(*deviceList, useCMPAtom, true, 4, &useCMPValue, nil);
			QTUnlockContainer(*deviceList);
			
			if (result == noErr)
				gGlobals->useCMP = useCMPValue;
				
		}
	}			

	// move all volatile atoms here
	nDVDevices = QTCountChildrenOfType( *deviceList, kParentAtomIsContainer, kIDHDeviceAtomType);
	for( i=0; i<nDVDevices; ++i)
	{
		QTAtom 				deviceAtomNew, nameAtomNew, deviceIDAtom;
		QTAtom 				deviceAtomOld, nameAtomOld;
		DeviceDescription	*deviceDescriptionPtr;
		IDHDeviceID			deviceID;
		UInt8				newName[256];
		SInt32				actualSize;
		
		// get the client supplied atoms
		deviceAtomNew = QTFindChildByIndex( *deviceList, kParentAtomIsContainer, kIDHDeviceAtomType, i + 1, nil);
		FailIf( deviceAtomNew == nil, Exit);
		
		nameAtomNew = QTFindChildByIndex( *deviceList, deviceAtomNew, kIDHNameAtomType, 1, nil);
		FailIf( nameAtomNew == nil, Exit);
		
		deviceIDAtom = QTFindChildByIndex( *deviceList, deviceAtomNew, kIDHDeviceIDType, 1, nil);
		FailIf( deviceIDAtom == nil, Exit);
		
		QTLockContainer( *deviceList);

		QTCopyAtomDataToPtr( *deviceList, deviceIDAtom, true, sizeof( IDHDeviceID), &deviceID, nil);
		
		QTUnlockContainer( *deviceList);
		
		// find the local copy of this device container
		result = findDeviceDescriptionforDevice( ih, deviceID, &deviceDescriptionPtr);
		FailWithVal( result != noErr, Exit, result);		

		deviceAtomOld = QTFindChildByIndex( deviceDescriptionPtr->deviceContainer, kParentAtomIsContainer, kIDHDeviceAtomType, 1, nil);
		FailIf( deviceAtomOld == nil, Exit);
		
		nameAtomOld = QTFindChildByIndex( deviceDescriptionPtr->deviceContainer, deviceAtomOld, kIDHNameAtomType, 1, nil);
		FailIf( nameAtomOld == nil, Exit);

		// get new name
		QTLockContainer( *deviceList);
		result = QTCopyAtomDataToPtr(*deviceList, nameAtomNew, true, 256, newName, &actualSize);
		QTUnlockContainer( *deviceList);
		FailWithVal( result != noErr, Exit, result);

		// update prefs file with new name
		// JKL, don't want to do this for now since FCP might be using this routine to toggle CMP
//		updateCameraName(&deviceDescriptionPtr->uniqueID, newName);
		
		// copy the new data into the current atom
		result = QTReplaceAtom( deviceDescriptionPtr->deviceContainer, nameAtomOld, *deviceList, nameAtomNew);
		FailWithVal( result != noErr, Exit, result);
	}

Exit:	
	return result;
}																		

// can be called synchronously at task level only
pascal ComponentResult	IDHWrite( ComponentInstance ihc, IDHParameterBlock *pb)
{
	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
	OSErr 						result = noErr;
	DeviceDescription			*deviceDescriptionPtr;
	Boolean						synchronous = false;
	IsochComponentGlobalsPtr	gGlobals = ih->gGlobals;
	
	RecordEventLogger( 'isoc', 'writ', 0, 0);

	FailWithAction( pb == nil, result = paramErr, Exit);

	// can't do sync writes at non-task level
	FailWithAction( pb->completionProc == nil && CurrentExecutionLevel() != kTaskLevel, result = paramErr, Exit);	

	// check for illegal condition
	FailWithAction( pb->completionProc == nil && pb->buffer == nil, result = paramErr, Exit);

	FailWithAction( ih->deviceID == nil, result = kIDHErrDeviceNotConfigured, Exit);	

	result = findDeviceDescriptionforDevice( ih, ih->deviceID, &deviceDescriptionPtr);
	FailWithVal( result != noErr, Exit, result);
		
	FailWithAction( deviceDescriptionPtr->active == false, result = kIDHErrDeviceDisconnected, Exit);

	if( deviceDescriptionPtr->writeIsochNotificationID == nil)
	{
		result = IDHNewNotification( (ComponentInstance) gGlobals->localInstance, ih->deviceID, writeNotifyProc,
				(void *) ih->gGlobals, &deviceDescriptionPtr->writeIsochNotificationID );
		FailWithVal( result != noErr, Exit, result);
	}
	
	if ( deviceDescriptionPtr->writeIsochNotificationID )
	{
		result = IDHNotifyMeWhen( (ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr->writeIsochNotificationID,
				kIDHPrivateEventWriteComplete);
		FailWithVal( result != noErr, Exit, result);
	}
	
	if( pb->completionProc == nil) // synchronous write
	{
		synchronous = true;
		
		pb->completionProc 	= localCompletionRoutine;
		pb->refCon 			= deviceDescriptionPtr;
		
		deviceDescriptionPtr->complete = false;
	}
	
	PBEnqueueLast( (QElemPtr) pb, ih->writeQueue);
		
	if( synchronous) // synchronous write
	{
		AbsoluteTime 	expirationTime;
		TimerID			theTimer;
		
		ih->timeout = false;
		
		// expiration is current time + 1/15 second (2 frame times)
		expirationTime = AddDurationToAbsolute( kTimeoutDuration, UpTime());
		
		result = SetInterruptTimer( &expirationTime, myTimerHandler, ih, &theTimer);
		FailWithVal( result != noErr, Exit, result);		

		while( deviceDescriptionPtr->complete == false && ih->timeout == false)	// loop until complete or timeout
			;
		
		expirationTime = DurationToAbsolute( 0);
		CancelTimer( theTimer, &expirationTime);

		pb->completionProc	= nil;
		
		FailWithAction( ih->timeout == true, result = kIDHErrDeviceTimeout, Exit); //еее correct error code
	}
	
Exit:

	return result;	
}

#pragma mark -
#pragma mark ееееееееее IDHDV Private Component Calls  ееееееееее

pascal ComponentResult	IDHDVPrivateCall( ComponentInstance ihc, IDHDVPrivateSelector selector, void *privateData)
{
	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
	OSErr 						result = noErr;
	
	switch (selector)
	{
		case kIDHDVPrivateSelectorDeviceAdded:
			RecordEventLogger( 'isoc', 'get ', 'priv', 'dadd');
			result = handleDeviceAdded(ihc, (GDFDeviceEventData*) privateData);
			break;
		
		case kIDHDVPrivateSelectorDeviceRemoved:
			RecordEventLogger( 'isoc', 'get ', 'priv', 'remv');
			result = handleDeviceRemoved(ihc, (GDFDeviceEventData*) privateData);
			break;
	}
	
	return result;
}

#pragma mark -
#pragma mark ееееееееее IDH Internal Calls  ееееееееее

OSErr addBuffersToQueue( DeviceDescription *deviceDescriptionPtr)
{
	QElemPtr 		qelem;
	OSErr 			result = noErr;
	QueueElement	*qep;
	short			j;
	
	if( countQueueElements( deviceDescriptionPtr->freeQueue) > 0 || countQueueElements( deviceDescriptionPtr->usedQueue) > 0)	// are there already buffers?
	{
		while( PBDequeueFirst( deviceDescriptionPtr->usedQueue, &qelem) == noErr)	// move buffers from used to free queue
		{
			((QueueElement *)qelem)->locks = 0;
			
			PBEnqueueLast( qelem, deviceDescriptionPtr->freeQueue);
		}
	}
	else
	{
		for( j=0; j<kBuffersPerDevice; ++j)	// add buffers to this devices queues
		{
			qep = (QueueElement *) NewPtrSysClear( sizeof( QueueElement));
			FailWithAction( qep == nil, result = MemError(), Exit);
			
			qep->buffer = NewPtrSys( (deviceDescriptionPtr->standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize);
			FailWithAction( qep->buffer == nil, result = MemError(), Exit);
			
			qep->locks = 0;	// initialize to no client locks
			
			PBEnqueueLast( (QElemPtr) qep, deviceDescriptionPtr->freeQueue);
		}
		
		deviceDescriptionPtr->nQueueElements = kBuffersPerDevice;	// save number of buffers
	}
	
Exit:
	return result;
	
BadExit:
	// dispose buffers if error
	while( PBDequeueFirst( deviceDescriptionPtr->freeQueue, (QElemPtr *) &qep) == noErr)
	{
		if( qep)
		{
			if( qep->buffer)
				DisposePtr( qep->buffer);
				
			DisposePtr( (char *) qep);
		}
	}
	
	while( PBDequeueFirst( deviceDescriptionPtr->usedQueue, (QElemPtr *) &qep) == noErr)
	{
		if( qep)
		{
			if( qep->buffer)
				DisposePtr( qep->buffer);
				
			DisposePtr( (char *) qep);
		}
	}
	
	deviceDescriptionPtr->nQueueElements = 0;

	return result;	
}

OSErr closeDeviceControl( ComponentInstance ihc, DeviceDescriptionPtr deviceDescriptionPtr)
{
	OSErr 						result = noErr;
	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
	IsochComponentGlobalsPtr	gGlobals = ih->gGlobals;
	
	if( deviceDescriptionPtr->deviceControlInstance)
	{
		if( --deviceDescriptionPtr->deviceControlCount <= 0)
		{
			deviceDescriptionPtr->deviceControlCount = 0;
		
			result = IDHCancelNotification( (ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr->deviceControlNotificationID);
			FailMessage( result != noErr);
			
			result = IDHDisposeNotification((ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr->deviceControlNotificationID);
			FailMessage( result != noErr);
			deviceDescriptionPtr->deviceControlNotificationID = 0;

			result = DeviceControlDisableAVCTransactions(deviceDescriptionPtr->deviceControlInstance);
			result = DeviceControlSetDeviceConnectionID(deviceDescriptionPtr->deviceControlInstance, 0);		

			CloseComponent(deviceDescriptionPtr->deviceControlInstance);

			deviceDescriptionPtr->deviceControlInstance = nil;
		}
	}

Exit:
	return result;
}

OSErr checkSeed( IsochComponentGlobalsPtr gGlobals, QTAtomSpec *configID)
{
	QTAtom 		seedAtom;
	OSErr 		result = noErr;
	UInt32		seed;
	
	// look for device in device container
	seedAtom = QTFindChildByIndex( configID->container, kParentAtomIsContainer, kIDHSeedAtomType, 1, nil);
	FailWithAction( seedAtom == nil, result = kIDHErrDeviceList, Exit);
	
	QTLockContainer( configID->container);

	// get the value of the devicePresent atom
	QTCopyAtomDataToPtr( configID->container, seedAtom, true, sizeof( seed), &seed, nil);
		
	QTUnlockContainer( configID->container);

	// seed has expired?
	if( seed != gGlobals->seed)
	{
		result = kIDHErrDeviceList;
		goto Exit;
	}
	
Exit:
	return result;	
}

int countQueueElements( QHdrPtr queue)
{
	QElem *qptr;
	int nElements = 0;
	
	qptr = queue->qHead;
	while( qptr)
	{
		++nElements;
		qptr = qptr->qLink;
	}
	
	return nElements;
}

// called when a device is added/removed and updates device control clientID
OSStatus deviceControlCallback( IDHGenericEvent *event, void *userData)
{
	OSErr 						result = noErr;
	DeviceDescription			*deviceDescriptionPtr = (DeviceDescriptionPtr) userData;
	IsochComponentGlobalsPtr	gGlobals = deviceDescriptionPtr->componentGlobals;
	
	RecordEventLogger( 'isoc', 'devc', 'calb', event->eventHeader.event);
	
	switch( event->eventHeader.event)
	{
		case kIDHEventDeviceAdded:
			
			result = DeviceControlDisableAVCTransactions(deviceDescriptionPtr->deviceControlInstance);
			FailMessageVal( result != noErr, result);

			result = DeviceControlSetDeviceConnectionID(deviceDescriptionPtr->deviceControlInstance, 
						(DeviceConnectionID) deviceDescriptionPtr->fwClientID);
			FailWithVal( result != noErr, Exit, result);
				
			result = DeviceControlEnableAVCTransactions(deviceDescriptionPtr->deviceControlInstance);
			FailWithVal( result != noErr, Exit, result);

			break;
			
		case kIDHEventDeviceRemoved:
			result = DeviceControlDisableAVCTransactions(deviceDescriptionPtr->deviceControlInstance);
			FailWithVal( result != noErr, Exit, result);

			break;
			
		default:
			FailMessage( (event->eventHeader.event & (kIDHEventDeviceRemoved | kIDHEventDeviceAdded)) == nil);
			break;
	}
	
	result = IDHNotifyMeWhen( (ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr->deviceControlNotificationID,
				kIDHEventDeviceAdded | kIDHEventDeviceRemoved);
	FailWithVal( result != noErr, Exit, result);
	
Exit:
	return result;	
}

OSErr doAVCTransaction( FWReferenceID refID, DVCTransactionParams* inTransaction)
{
	FWCommandObjectID 	commandObjectID;
	OSErr 				result = noErr;
	FCPResponseHandler	*responseHandler = 0;
	UInt32 				fwCommandFlags = kFWCommandSyncFlag;

	// allocate command object for sending FCP commands synchronously.
	result = FWAllocateFCPCommandObject(&commandObjectID);
	FailWithVal( result != noErr, Exit1, result);

	// Set up FCP command params to tell camera to do something.
	result = FWSetFWCommandParams(	commandObjectID,							// objectID
									refID,										// ref ID
									fwCommandFlags,								// cmd flags
									nil,										// completion proc
									0);											// completion data
	FailWithVal( result != noErr, Exit, result);
									

	result = FWSetFCPCommandParams(	commandObjectID,							// objectID
									inTransaction->commandBufferPtr,			// cmd buffer
									inTransaction->commandLength,				// cmd length
									inTransaction->responseBufferPtr,			// response buffer
									inTransaction->responseBufferSize,			// response size
									100 * durationMillisecond,					// timeout
									8,											// max retries
									0,											// transfer flags
									responseHandler );							// response handler
	FailWithVal( result != noErr, Exit, result);
									
	// send the FCP command
	result = FWSendFCPCommand(commandObjectID);
	FailWithVal( result != noErr, Exit, result);

Exit:	
	FWDeallocateFWCommandObject(commandObjectID);

Exit1:
	return result;
}

OSErr findDeviceDescriptionforDevice( IsochComponentInstancePtr ih, UInt32 deviceID, DeviceDescription **deviceDescription)
{
	OSErr 						result = noErr;
	IsochComponentGlobalsPtr 	gGlobals = ih->gGlobals;
	short						i;
	
	*deviceDescription = nil;	
	for( i=0; i<kMaxDevicesActive; ++i)
	{
		if( gGlobals->deviceDescription[i].deviceID == deviceID)
		{
			*deviceDescription = &gGlobals->deviceDescription[i];
			break;
		}
	}
	
	if( *deviceDescription == nil)
		result = kIDHErrInvalidDeviceID;
	
Exit:	
	return result;
}

//еее do a super find atom someday
OSErr findAtom( QTAtomSpec *atomSpec, OSType theType, QTAtom *theAtom)
{
	OSErr result = noErr;
	OSType type;
	QTAtom atom;

	atom = atomSpec->atom;
	
	result = QTGetAtomTypeAndID( atomSpec->container, atom, (long *) &type, nil);
	FailWithVal( result != noErr, Exit, result);
	
	while( type != kIDHDeviceAtomType && type != theType)
	{
		atom = QTGetAtomParent( atomSpec->container, atom); // should be isoch atom
		FailWithAction( atom == nil || atom == -1, result = kIDHErrDeviceList, Exit);
		
		result = QTGetAtomTypeAndID( atomSpec->container, atom, (long *) &type, nil);
		FailWithVal( result != noErr, Exit, result);		
	}
	
	if( theType == type)
	{
		*theAtom = atom;
		result = noErr;
	}	
	else
	{
		*theAtom = nil;
		result = kIDHErrDeviceList;
	}

Exit:		
	return result;
}

OSErr getDeviceID( QTAtomSpec *configID, UInt32 *deviceID)
{
	OSErr result = noErr;
	QTAtom deviceAtom;
	
	*deviceID = nil;
	
	result = findAtom( configID, kIDHDeviceAtomType, &deviceAtom);
	FailWithVal( result != noErr, Exit, result);
	
	result = QTGetAtomTypeAndID( configID->container, deviceAtom, nil, (long *) deviceID);
	FailWithVal( result != noErr, Exit, result);
		
Exit:
	return result;
}


OSErr getDeviceStandard( FWReferenceID clientID, UInt32 * pStandard )
{
	AVCCTSFrameStruct		avcFrame;
	DVCTransactionParams	transactionParams;
	UInt8					responseBuffer[ 16 ];
	OSErr					theErr = noErr;
	UInt32					currentSignal, AVCStatus;
	
	*pStandard = ntscIn;
	
	// fill up the avc frame
	avcFrame.cmdType_respCode	= kAVCStatusInquiryCommand;
	avcFrame.headerAddress 		= 0x20;						// for now
	avcFrame.opcode 			= kAVCOutputSignalModeOpcode;
	avcFrame.operand[ 0 ] 		= kAVCSignalModeDummyOperand;
	
	// fill up the transaction parameter block
	transactionParams.commandBufferPtr		= (Ptr) &avcFrame;
	transactionParams.commandLength			= 4;
	transactionParams.responseBufferPtr		= (Ptr) responseBuffer;
	transactionParams.responseBufferSize	= 16;
	transactionParams.responseHandler		= nil;
	
//	some cameras need some time before they can respond to this command
	DelayFor ( 1 * durationSecond );
	
	theErr = setupAVCTransaction( clientID, &transactionParams );
	FailWithVal( theErr != noErr, Exit, theErr);
	
	currentSignal = ((responseBuffer[ 2 ] << 8) | responseBuffer[ 3 ]);
	AVCStatus = responseBuffer[ 0 ];
	
	switch (currentSignal & 0x000000ff) 
	{
		case kAVCSignalModeSD525_60: 
		case kAVCSignalModeSDL525_60:
		case kAVCSignalModeHD1125_60: 
			*pStandard = ntscIn;
			break;
	
		case kAVCSignalModeSD625_50: 
		case kAVCSignalModeSDL625_50: 
		case kAVCSignalModeHD1250_50: 
			*pStandard = palIn;
			break;
	}
	
Exit:

// JKL, because of devices that do not have device control, it is probably not safe
// to return an error here. Just default to NTSC. Need a better solution for this:
// user input? examining the stream? If this is just for setting the buffer size
// might be better to waste 100k in the NTSC case and just use PAL buffer size.
	if (theErr != noErr)
	{
		*pStandard = ntscIn;
		theErr = noErr;
	}
	
	return theErr;	
}

OSStatus localCompletionRoutine( IDHGenericEvent *event, void *userData)
{
#pragma unused (event)

	DeviceDescription *deviceDescriptionPtr = (DeviceDescription *) userData;
	
	deviceDescriptionPtr->complete = true;	// tell the read/write operation that operation is complete
	
	return noErr;
}

OSStatus myTimerHandler( void *p1, void *p2)
{
#pragma unused( p2)

	IsochComponentInstancePtr ih = (IsochComponentInstancePtr) p1;

	ih->timeout = true;
	
	return noErr;
}

OSErr postEvent(IsochComponentGlobals* gGlobals,
		IDHDeviceID deviceID,
		IDHEvent event,
		void* privateData)
{
	UInt32 i;
	IDHDeviceConnectionEvent connectionEvent;
	OSErr error = noErr;

	// We now have two broad classifications of events - ones that need to be
	// reported ASAP, which are stream related:
	//
	// 		kIDHPrivateEventReadComplete
	//		kIDHPrivateEventWriteComplete
	//
	// and ones that are device management related, whose notifications will
	// probably generate massive amounts of task-level only Toolbox calls:
	//
	//		kIDHEventDeviceAdded
	//		kIDHEventDeviceRemoved
	//		kIDHEventReadEnabled
	//		kIDHEventReadDisabled
	//		kIDHEventWriteEnabled
	//		kIDHEventWriteDisabled
	//
	// kIDHPrivateEventReadComplete and kIDHPrivateEventWriteComplete are posted to a secondary
	// interrupt handler. All other events are handled immediately.
	

	RecordEventLogger( 'isoc', 'post', deviceID, event);

	for (i = 0; i < kMaxNotifications ; ++i)
	{
		ClientNotification* clientNotification = &gGlobals->clientNotification[i];
		IDHDeviceID clientDeviceID = clientNotification->deviceID;
		IDHEvent wantedEvents = clientNotification->events;

		if (event & wantedEvents)
		{
			// Send notification if the device matches the client's desired device
			// or client specified all devices.
			if ((kIDHDeviceIDEveryDevice == clientDeviceID) || 
				(deviceID == clientDeviceID) )
			{				
				// we currently only support a one-shot notification, like clock callbacks
				clientNotification->events = 0;
				
				switch(event)
				{
					case kIDHPrivateEventReadComplete:
					case kIDHPrivateEventWriteComplete:

						(*clientNotification->notificationProc)
								((IDHGenericEvent*)privateData,
								clientNotification->userData);
						break;
			
					case kIDHEventDeviceChanged:
					case kIDHEventDeviceAdded:
					case kIDHEventDeviceRemoved:
					case kIDHEventReadEnabled:
					case kIDHEventReadDisabled:
					case kIDHEventWriteEnabled:
					case kIDHEventWriteDisabled:

						connectionEvent.eventHeader.event = event;
						connectionEvent.eventHeader.deviceID = deviceID;
						connectionEvent.eventHeader.notificationID =
								(UInt32)clientNotification;
						
						(*clientNotification->notificationProc)
								((IDHGenericEvent*)&connectionEvent,
								clientNotification->userData);
						break;

					default:
						RecordEventLogger( 'isoc', 'post', '????', event);
						break;
				}
	
			}
		}
	}

	return error;

}


OSStatus postEventSIH(void* p1, void* p2)
{
	ClientNotification			*clientNotification = (ClientNotification *) p1;
 	IDHGenericEvent				*privateData =  (IDHGenericEvent *) p2;
	OSStatus					status = noErr;

	// call the routine
	(*clientNotification->notificationProc) (privateData, clientNotification->userData);

	return status;
}


OSStatus readNotifyProc( IDHGenericEvent *event, void *userData)
{
	OSErr 						result = noErr;
	IsochComponentGlobalsPtr	gGlobals = userData;
	IDHDVCompleteEvent*	 		readCompletePtr = (IDHDVCompleteEvent*) event;
	DeviceDescription			*deviceDescriptionPtr = nil;
	short						i;
	QueueElementPtr				qep;
	
	RecordEventLogger( 'isoc', 'read', 'comp', ((char *) readCompletePtr->frameBuffer)[640] & 0xf);
	
	FailIf( event->eventHeader.event != kIDHPrivateEventReadComplete, Exit);
			
	// find the device description that belongs to this read callback
	deviceDescriptionPtr = nil;
	for( i=0; i<kMaxDevicesActive; ++i)
	{
		if( gGlobals->deviceDescription[i].deviceID == event->eventHeader.deviceID)
		{
			deviceDescriptionPtr = &gGlobals->deviceDescription[i];
			break;
		}
	}
	
	FailIf( deviceDescriptionPtr == nil, Exit);	// couldn't find device in tree
	
	// find a buffer to copy the data into
	if( (result = PBDequeueFirst( deviceDescriptionPtr->freeQueue, (QElemPtr *) &qep)) != noErr)
		if( (result = PBDequeueFirst( deviceDescriptionPtr->usedQueue, (QElemPtr *) &qep)) != noErr) //еее changes when lock
		{
			FailWithVal( result != noErr, Exit, result); // no queue elems available
		}
		
	qep->locks = 0;	// reset lock

	// make sure we have enuf room
	FailIf( readCompletePtr->bufferSize > kPALCompressedBufferSize, Exit);
//	(deviceDescriptionPtr->standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize, Exit);
	
	// copy the data and save the size
	BlockMoveData( readCompletePtr->frameBuffer, qep->buffer, readCompletePtr->bufferSize);
	qep->size = readCompletePtr->bufferSize;
	
	// find all of instances that need to be serviced for this device
	for( i=0; i<kMaxInstances; ++i)
	{
		IsochComponentInstancePtr	ih = deviceDescriptionPtr->instancePtr[i];
		IDHParameterBlock 			*readRequest;
			
		if( deviceDescriptionPtr->instancePtr[i] == nil)	// no instance?
			continue;

		// get a read request
		if( PBDequeueFirst( ih->readQueue, (QElemPtr *) &readRequest) != noErr)
			continue;
		
		readRequest->actualCount = 0;
		readRequest->result = noErr;
		
		// does the user want an internal buffer or copy to user buffer
		if( readRequest->buffer == nil)
		{
			RecordEventLogger( 'isoc', 'redc', 'read', 'nil ');
			
			readRequest->buffer 		= readCompletePtr->frameBuffer;	// return internal buffer
			readRequest->actualCount 	= readCompletePtr->bufferSize;
			
			++qep->locks;	// add a lock to this buffer
		}
		else
		{
			RecordEventLogger( 'isoc', 'redc', 'read', 'buff');

			if( readCompletePtr->bufferSize <= readRequest->requestedCount) // do we have enuf room?
			{					
				// copy the buffer into client buffer
				BlockMoveData( readCompletePtr->frameBuffer, readRequest->buffer, readCompletePtr->bufferSize);
				readRequest->actualCount = readCompletePtr->bufferSize;
			}	
		}
		
		// call the client callback
		result = (*readRequest->completionProc)( (IDHGenericEvent *) readRequest, readRequest->refCon);

	} // end of processing instances

	if( qep->locks > 0)	// if this buffer has locks, move it to the usedQueue else requeue on free queue
		PBEnqueueLast( (QElemPtr) qep, deviceDescriptionPtr->usedQueue);
	else
		PBEnqueueLast( (QElemPtr) qep, deviceDescriptionPtr->freeQueue);	
		
	// reset read complete notification
	result = IDHNotifyMeWhen( (ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr->readIsochNotificationID, 
				kIDHPrivateEventReadComplete);
		
Exit:	
	return result;
}

OSErr setupAVCTransaction( FWReferenceID refID, DVCTransactionParams *pParams )
{
	DVCTransactionParams		transactionParams;
	OSErr						error = noErr;
	
	// fill out the internal transaction block
	transactionParams.commandBufferPtr		= pParams->commandBufferPtr;
	transactionParams.commandLength			= pParams->commandLength;
	transactionParams.responseBufferPtr		= pParams->responseBufferPtr;
	transactionParams.responseBufferSize	= pParams->responseBufferSize;
	transactionParams.responseHandler		= nil; //еее pParams->responseHandler;

	error = doAVCTransaction( refID, &transactionParams );
	FailWithVal( error != noErr, Exit, error);
	
Exit:
	return( error );
}

OSErr setupVideoAtoms( QTAtomContainer container, QTAtom isocAtom, UInt32 standard) // add DV NTSC config
{	
	OSErr 					result = noErr;
	QTAtom 					configAtom;	
	OSType 					type;
	long 					size;
	float 					interval;
	long					direction;
	IDHDimension 			dimension;
	IDHResolution 			resolution;
	Fixed 					refresh;
	OSType 					pixel;
	OSType 					decoType;
	Component 				decoComponent;	
	ComponentDescription 	compDescrip;
	
	// create a vide NTSC mode
	result = QTInsertChild( container, isocAtom, kIDHIsochModeAtomType,
			0, 0, 0, nil, &configAtom);
	FailWithVal( result != noErr, Exit, result);
	
	type = kIDHVideoMediaAtomType;
	result = QTInsertChild( container, configAtom, kIDHIsochMediaType,
			0, 0, sizeof( type), &type, nil);
	FailWithVal( result != noErr, Exit, result);

	// for compatibility with Hemingway releases prior to our d13 release
	result = QTInsertChild( container, configAtom, kIDHNameAtomType,
			0, 0, 3, "\pDV", nil);
	FailWithVal( result != noErr, Exit, result);
	
	type = (standard == ntscIn)?'DVC ':'DVCP';
	result = QTInsertChild( container, configAtom, kIDHDataTypeAtomType,
			0, 0, sizeof( type), &type, nil);
	FailWithVal( result != noErr, Exit, result);
	
	size = (standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize;
	result = QTInsertChild( container, configAtom, kIDHDataSizeAtomType,
			0, 0, sizeof( size), &size, nil);
	FailWithVal( result != noErr, Exit, result);
	
	size = (standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize;
	result = QTInsertChild( container, configAtom, kIDHDataBufferSizeAtomType,
			0, 0, sizeof( size), &size, nil);
	FailWithVal( result != noErr, Exit, result);

	interval = 29.97;
	result = QTInsertChild( container, configAtom, kIDHDataIntervalAtomType,
			0, 0, sizeof( interval), &interval, nil);
	FailWithVal( result != noErr, Exit, result);
	
	direction = kIDHDataTypeIsInputAndOutput;
	result = QTInsertChild( container, configAtom, kIDHDataIODirectionAtomType,
			0, 0, sizeof( direction), &direction, nil);
	FailWithVal( result != noErr, Exit, result);

	dimension.x = 720; dimension.y = (standard == ntscIn)?480:576;
	result = QTInsertChild( container, configAtom, kIDHVideoDimensionsAtomType,
			0, 0, sizeof( dimension), &dimension, nil);
	FailWithVal( result != noErr, Exit, result);
	
	resolution.x = 72 << 16; resolution.y = 72 << 16;
	result = QTInsertChild( container, configAtom, kIDHVideoResolutionAtomType,
			0, 0, sizeof( resolution), &resolution, nil);
	FailWithVal( result != noErr, Exit, result);

	refresh = (29 << 16) + 97; //еее
	result = QTInsertChild( container, configAtom, kIDHVideoRefreshRateAtomType,
			0, 0, sizeof( refresh), &refresh, nil);
	FailWithVal( result != noErr, Exit, result);

	pixel = 'dv  '; //еее
	result = QTInsertChild( container, configAtom, kIDHVideoPixelTypeAtomType,
			0, 0, sizeof( pixel), &pixel, nil);
	FailWithVal( result != noErr, Exit, result);

//еее	kIDHVideoDecompressorsAtomType		= FOUR_CHAR_CODE('deco'),

	decoType = (standard == ntscIn)?'dvc ':'dvcp';
	result = QTInsertChild( container, configAtom, kIDHVideoDecompressorTypeAtomType,
			0, 0, sizeof( decoType), &decoType, nil);
	FailWithVal( result != noErr, Exit, result);
	
	compDescrip.componentType = 'imdc';
	compDescrip.componentSubType = decoType;
	compDescrip.componentManufacturer = 0;
	compDescrip.componentFlags = 0;
	compDescrip.componentFlagsMask = 0;
	
	decoComponent = FindNextComponent( nil, &compDescrip);
	result = QTInsertChild( container, configAtom, kIDHVideoDecompressorComponentAtomType,
			0, 0, sizeof( decoComponent), &decoComponent, nil);
	FailWithVal( result != noErr, Exit, result);
	
Exit:				
	return result;				
}

OSErr setup48kAudioAtoms( QTAtomContainer container, QTAtom isocAtom, UInt32 standard)
{	
	OSErr 		err;
	QTAtom 		configAtom;
	
	StringPtr 	name;
	OSType 		type;
	long 		size;
	Fixed 		rate;
	float 		interval;
	long 		direction;
	
	// create a vide NTSC mode
	err = QTInsertChild( container, isocAtom, kIDHIsochModeAtomType,
			0, 0, 0, nil, &configAtom);
	FailWithVal( err != noErr, Exit, err);
	
	type = kIDHSoundMediaAtomType;
	err = QTInsertChild( container, configAtom, kIDHIsochMediaType,
			0, 0, sizeof( type), &type, nil);
	FailWithVal( err != noErr, Exit, err);

	name = "\pDV-48khz";
	err = QTInsertChild( container, configAtom, kIDHNameAtomType,
			0, 0, name[0], name, nil);
	FailWithVal( err != noErr, Exit, err);
	
	type = 'DV48';
	err = QTInsertChild( container, configAtom, kIDHDataTypeAtomType,
			0, 0, sizeof( type), &type, nil);
	FailWithVal( err != noErr, Exit, err);
	
	size = (standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize;
	err = QTInsertChild( container, configAtom, kIDHDataSizeAtomType,
			0, 0, sizeof( size), &size, nil);
	FailWithVal( err != noErr, Exit, err);
	
	size = (standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize;
	err = QTInsertChild( container, configAtom, kIDHDataBufferSizeAtomType,
			0, 0, sizeof( size), &size, nil);
	FailWithVal( err != noErr, Exit, err);

	interval = 29.97;
	err = QTInsertChild( container, configAtom, kIDHDataIntervalAtomType,
			0, 0, sizeof( interval), &interval, nil);
	FailWithVal( err != noErr, Exit, err);
	
	direction = kIDHDataTypeIsInputAndOutput;
	err = QTInsertChild( container, configAtom, kIDHDataIODirectionAtomType,
			0, 0, sizeof( direction), &direction, nil);
	FailWithVal( err != noErr, Exit, err);

	size = 2;
	err = QTInsertChild( container, configAtom, kIDHSoundChannelCountAtomType,
			0, 0, sizeof( size), &size, nil);
	FailWithVal( err != noErr, Exit, err);
	
	size = 2;
	err = QTInsertChild( container, configAtom, kIDHSoundSampleSizeAtomType,
			0, 0, sizeof( size), &size, nil);
	FailWithVal( err != noErr, Exit, err);

	rate = rate44khz;
	err = QTInsertChild( container, configAtom, kIDHSoundSampleRateAtomType,
			0, 0, sizeof( rate), &rate, nil);
	FailWithVal( err != noErr, Exit, err);
	
Exit:
	return err;	
} // sound 1 config

OSErr setup32kAudioAtoms( QTAtomContainer container, QTAtom isocAtom, UInt32 standard)
{	
	OSErr err;
	QTAtom configAtom;
	
	StringPtr 	name;
	OSType 		type;
	long 		size;
	Fixed 		rate;
	float 		interval;
	long 		direction;

	// create a vide NTSC mode
	err = QTInsertChild( container, isocAtom, kIDHIsochModeAtomType,
			0, 0, 0, nil, &configAtom);
	FailWithVal( err != noErr, Exit, err);

	type = kIDHSoundMediaAtomType;
	err = QTInsertChild( container, configAtom, kIDHIsochMediaType,
			0, 0, sizeof( type), &type, nil);
	FailWithVal( err != noErr, Exit, err);

	name = "\pDV-32khz";
	err = QTInsertChild( container, configAtom, kIDHNameAtomType,
			0, 0, name[0], name, nil);
	FailWithVal( err != noErr, Exit, err);

	type = 'DV32';
	err = QTInsertChild( container, configAtom, kIDHDataTypeAtomType,
			0, 0, sizeof( type), &type, nil);
	FailWithVal( err != noErr, Exit, err);

	size = (standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize;
	err = QTInsertChild( container, configAtom, kIDHDataSizeAtomType,
			0, 0, sizeof( size), &size, nil);
	FailWithVal( err != noErr, Exit, err);

	size = (standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize;
	err = QTInsertChild( container, configAtom, kIDHDataBufferSizeAtomType,
			0, 0, sizeof( size), &size, nil);
	FailWithVal( err != noErr, Exit, err);

	interval = 29.97;
	err = QTInsertChild( container, configAtom, kIDHDataIntervalAtomType,
			0, 0, sizeof( interval), &interval, nil);
	FailWithVal( err != noErr, Exit, err);

	direction = kIDHDataTypeIsInputAndOutput;
	err = QTInsertChild( container, configAtom, kIDHDataIODirectionAtomType,
			0, 0, sizeof( direction), &direction, nil);
	FailWithVal( err != noErr, Exit, err);

	size = 4;
	err = QTInsertChild( container, configAtom, kIDHSoundChannelCountAtomType,
			0, 0, sizeof( size), &size, nil);
	FailWithVal( err != noErr, Exit, err);

	size = 2;
	err = QTInsertChild( container, configAtom, kIDHSoundSampleSizeAtomType,
			0, 0, sizeof( size), &size, nil);
	FailWithVal( err != noErr, Exit, err);

	rate = 32000 << 16;
	err = QTInsertChild( container, configAtom, kIDHSoundSampleRateAtomType,
			0, 0, sizeof( rate), &rate, nil);
	FailWithVal( err != noErr, Exit, err);
	
Exit:
	return err;	
} // sound 2 config 

OSErr setup44kAudioAtoms( QTAtomContainer container, QTAtom isocAtom, UInt32 standard)
{	
	OSErr err;
	QTAtom configAtom;
	
	StringPtr 	name;
	OSType 		type;
	long 		size;
	Fixed 		rate;
	float 		interval;
	long 		direction;

	// create a vide NTSC mode
	err = QTInsertChild( container, isocAtom, kIDHIsochModeAtomType,
			0, 0, 0, nil, &configAtom);
	FailWithVal( err != noErr, Exit, err);

	type = kIDHSoundMediaAtomType;
	err = QTInsertChild( container, configAtom, kIDHIsochMediaType,
			0, 0, sizeof( type), &type, nil);
	FailWithVal( err != noErr, Exit, err);

	name = "\pDV-44khz";
	err = QTInsertChild( container, configAtom, kIDHNameAtomType,
			0, 0, name[0], name, nil);
	FailWithVal( err != noErr, Exit, err);

	type = 'DV44';
	err = QTInsertChild( container, configAtom, kIDHDataTypeAtomType,
			0, 0, sizeof( type), &type, nil);
	FailWithVal( err != noErr, Exit, err);

	size = (standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize;
	err = QTInsertChild( container, configAtom, kIDHDataSizeAtomType,
			0, 0, sizeof( size), &size, nil);
	FailWithVal( err != noErr, Exit, err);

	size = (standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize;
	err = QTInsertChild( container, configAtom, kIDHDataBufferSizeAtomType,
			0, 0, sizeof( size), &size, nil);
	FailWithVal( err != noErr, Exit, err);

	interval = 29.97;
	err = QTInsertChild( container, configAtom, kIDHDataIntervalAtomType,
			0, 0, sizeof( interval), &interval, nil);
	FailWithVal( err != noErr, Exit, err);

	direction = kIDHDataTypeIsInputAndOutput;
	err = QTInsertChild( container, configAtom, kIDHDataIODirectionAtomType,
			0, 0, sizeof( direction), &direction, nil);
	FailWithVal( err != noErr, Exit, err);

	size = 4;
	err = QTInsertChild( container, configAtom, kIDHSoundChannelCountAtomType,
			0, 0, sizeof( size), &size, nil);
	FailWithVal( err != noErr, Exit, err);

	size = 2;
	err = QTInsertChild( container, configAtom, kIDHSoundSampleSizeAtomType,
			0, 0, sizeof( size), &size, nil);
	FailWithVal( err != noErr, Exit, err);

	rate = 44100 << 16;
	err = QTInsertChild( container, configAtom, kIDHSoundSampleRateAtomType,
			0, 0, sizeof( rate), &rate, nil);
	FailWithVal( err != noErr, Exit, err);
	
Exit:
	return err;	
} // sound 3 config 

OSErr updateDeviceList(IsochComponentInstancePtr ih, GDFDeviceEventData *pGDFData, IDHDeviceID *deviceID, Boolean doAdd)
{
	OSErr 						result = noErr;
	QTAtom 						deviceAtom, isocAtom;
	IsochComponentGlobalsPtr 	gGlobals = ih->gGlobals;
	Boolean						listChanged = false, foundDevice = false;
	DeviceDescription			*deviceDescriptionPtr;
	IDHDeviceStatus				deviceStatus;
	UInt32						standard;
	UInt32						devIndex;
	FWReferenceID				localFWReferenceID;
	ComponentDescription		clkDesc;
	
	RecordEventLogger( 'isoc', 'updt', pGDFData->driverRefNum, 0);

	++gGlobals->seed;
			
	if (doAdd)
	{
		// The refNum of a driver is NOT a constant IF the driver is allowed to close
		// once it has been opened. Our expert never allows the driver to be closed.
		// So use that refNum to search for the device in.
		// The same provision holds true for the RegEntryID so we could do that.

		// look for existing device
		for( devIndex=0; devIndex < gGlobals->nDevices; ++devIndex)
		{
			if (pGDFData->driverRefNum == gGlobals->deviceDescription[devIndex].refNum)
			{
				FWDriverID clientID = gGlobals->deviceDescription[devIndex].fwClientID;
				gGlobals->deviceDescription[devIndex].active = true;
				foundDevice = true;
				
				// max packet size for s100, set again in case clientID changes
				FWSetMaxPayloadSize(clientID, 512);
	
				// Driver/RegEntry ID would change if device wasplugged into a different
				// interface/card. Can fix in future by looking at UniqueID in "new"
				// devices and see if they match any of the old devices.
	
				// if there are more than one FW interfaces on the machine, its conceivable
				// that the same device could get plugged into a different interface
				// update the local FW reference just in case
				result = FWGetLocalFWReferenceIDFromFWReferenceID(clientID, &localFWReferenceID);
				FailWithVal( result != noErr, Exit, result);		
				gGlobals->deviceDescription[devIndex].localFWReferenceID = localFWReferenceID;

				*deviceID = gGlobals->deviceDescription[devIndex].deviceID;
			}
		}

		if (!foundDevice)
		{
			FWDriverID clientID;
			CSRNodeUniqueID csrNodeUniqueID;
			Str255 cameraName;
			
			deviceDescriptionPtr = &gGlobals->deviceDescription[gGlobals->nDevices];	

			result = registerDevice(
					&pGDFData->deviceRegEntryID,
					&clientID,
					deviceDescriptionPtr);
			
			FailIf(noErr != result, Exit);

			RecordEventLogger( 'isoc', 'updt', 'add ', 'reg ');

			result = FWGetUniqueID(clientID, &csrNodeUniqueID);
			FailIf( result != noErr, Exit);

			// add its description
			gGlobals->nDevices++;
	
			result = FWGetLocalFWReferenceIDFromFWReferenceID(clientID, &localFWReferenceID);
			FailWithVal( result != noErr, Exit, result);		
			
			*deviceID = gGlobals->nDevices;
			
			deviceDescriptionPtr->componentGlobals		= gGlobals;
			deviceDescriptionPtr->regEntryID 			= pGDFData->deviceRegEntryID;						
			deviceDescriptionPtr->refNum 				= pGDFData->driverRefNum;						
			deviceDescriptionPtr->deviceID 				= *deviceID;
			deviceDescriptionPtr->fwClientID 			= clientID;
			deviceDescriptionPtr->localFWReferenceID 	= localFWReferenceID;
			deviceDescriptionPtr->uniqueID.hi 			= csrNodeUniqueID.hi;
			deviceDescriptionPtr->uniqueID.lo 			= csrNodeUniqueID.lo;
			deviceDescriptionPtr->deviceControlNotificationID = 0;		
			
			// max packet size for s100
			FWSetMaxPayloadSize(clientID, 512);
	
			// add the device standard (PAL, NTSC)
			result = getDeviceStandard(clientID, &standard);
			FailWithVal( result != noErr, Exit, result);		

			deviceDescriptionPtr->standard 				= standard;			
			deviceDescriptionPtr->isochPortID 			= 0;
			deviceDescriptionPtr->outputIsochChannelID 	= 0;
			deviceDescriptionPtr->inputIsochChannelID 	= 0;
			deviceDescriptionPtr->pGlobalDVOutData 		= nil;
			deviceDescriptionPtr->pGlobalDVInData 		= nil;
			deviceDescriptionPtr->dclProgramID 			= 0;
			deviceDescriptionPtr->active 				= true;
			
			// allocate command objects that may be used at non-task level
			// jkl, do these need to be deallocated somewhere?
			result = FWAllocateIsochPortCommandObject(&deviceDescriptionPtr->isochPortCommandObjectID);
			FailWithVal( result != noErr, Exit, result);
			result = FWAllocateIsochPortCommandObject(&deviceDescriptionPtr->stopIsochPortCommandObjectID);
			FailWithVal( result != noErr, Exit, result);
			result = FWAllocateIsochPortCommandObject(&deviceDescriptionPtr->startIsochPortCommandObjectID);
			FailWithVal( result != noErr, Exit, result);
			result = FWAllocateAsynchCommandObject(&deviceDescriptionPtr->asynchCommandObjectID);
			FailWithVal( result != noErr, Exit, result);
			FWSetAsynchCommandMaxPayloadSize(deviceDescriptionPtr->asynchCommandObjectID, 512);

			// find clock component
			// wouldn't it be better for us to open an instance on OpenDevice, set FWClockPrivLocalReference, etc.
			clkDesc.componentType 			= clockComponentType;
			clkDesc.componentSubType 		= 'fwcy';
			clkDesc.componentManufacturer 	= 'appl';
			clkDesc.componentFlags 			= 0L;
			clkDesc.componentFlagsMask 		= 0L;

			deviceDescriptionPtr->clock = 0;
			deviceDescriptionPtr->clock = FindNextComponent( deviceDescriptionPtr->clock, &clkDesc);	// Look for FireWire clock component
			FailMessage( deviceDescriptionPtr->clock == nil);

			// create device description atom structure
			result = QTNewAtomContainer( &deviceDescriptionPtr->deviceContainer);
			FailWithVal( result != noErr, Exit, result);
				
			// add a device atom
			result = QTInsertChild( deviceDescriptionPtr->deviceContainer, kParentAtomIsContainer, kIDHDeviceAtomType,
						(long) *deviceID, 0, 0, nil, &deviceAtom);
			FailWithVal( result != noErr, Exit, result);
	
			// add the unique 64 bit FireWire GUID id to device atom
			result = QTInsertChild( deviceDescriptionPtr->deviceContainer, deviceAtom, kIDHUniqueIDType,
						0, 0, sizeof(CSRNodeUniqueID), &csrNodeUniqueID, nil);
			FailWithVal( result != noErr, Exit, result);
				
			result = cameraNameLookup(deviceDescriptionPtr, cameraName);	
			FailWithVal( result != noErr, Exit, result);
			result = QTInsertChild( deviceDescriptionPtr->deviceContainer, deviceAtom, kIDHNameAtomType,
						0, 0, cameraName[0] + 1, cameraName, nil);

			// add the IDH unique id to device atom
			result = QTInsertChild( deviceDescriptionPtr->deviceContainer, deviceAtom, kIDHDeviceIDType,
						0, 0, sizeof(deviceDescriptionPtr->deviceID), &deviceDescriptionPtr->deviceID, nil);
			FailWithVal( result != noErr, Exit, result);

			// create a device status structure and add it to the device atom
			deviceStatus.version 				= 1;
			deviceStatus.physicallyConnected 	= true;
			deviceStatus.readEnabled 			= false;
			deviceStatus.writeEnabled 			= false;
			deviceStatus.exclusiveAccess 		= false;
			deviceStatus.currentBandwidth 		= 1;
			deviceStatus.currentChannel 		= 0;
			deviceStatus.inputStandard			= 0;
			deviceStatus.deviceActive 			= false;
			
			result = QTInsertChild( deviceDescriptionPtr->deviceContainer, deviceAtom, kDVDeviceInfo,
						0, 0, sizeof( IDHDeviceStatus), &deviceStatus, nil);
			FailWithVal( result != noErr, Exit, result);
	
			// add isoch descriptions to structure
			result = QTInsertChild( deviceDescriptionPtr->deviceContainer, deviceAtom, kIDHIsochServiceAtomType,
						0, 0, 0, nil, &isocAtom);
			FailWithVal( result != noErr, Exit, result);
			
			// add the configs to the isoc atom
			result = setupVideoAtoms( deviceDescriptionPtr->deviceContainer, isocAtom, standard);
			FailWithVal( result != noErr, Exit, result);
				
			if( standard == ntscIn)
			{
				result = setup48kAudioAtoms( deviceDescriptionPtr->deviceContainer, isocAtom, standard);
				FailWithVal( result != noErr, Exit, result);
			
				result = setup32kAudioAtoms( deviceDescriptionPtr->deviceContainer, isocAtom, standard);
				FailWithVal( result != noErr, Exit, result);
			}
			else	// PAL audio
			{
				result = setup44kAudioAtoms( deviceDescriptionPtr->deviceContainer, isocAtom, standard);
				FailWithVal( result != noErr, Exit, result);			
			}
		}
	}
	else	// process device disabled
	{	
		RecordEventLogger( 'isoc', 'updt', 'disa', 0);
		
		for( devIndex=0; devIndex < gGlobals->nDevices; ++devIndex)	// find disabled device in list
		{
			if (gGlobals->deviceDescription[devIndex].refNum == pGDFData->driverRefNum)	// do we match?
			{
				DeviceDescription 	*deviceDescriptionPtr = &(gGlobals->deviceDescription[devIndex]);
				int					i;
				
				gGlobals->deviceDescription[devIndex].active = false;
				
				*deviceID = gGlobals->deviceDescription[devIndex].deviceID;
				
				RecordEventLogger( 'isoc', 'updt', 'disa', 'foun');

				// find all of instances that have pending I/O for this device
				for( i=0; i<kMaxInstances; ++i)
				{
					IsochComponentInstancePtr 	ih = deviceDescriptionPtr->instancePtr[i];
					IDHParameterBlock 			*writeRequest, *readRequest;
						
					if( deviceDescriptionPtr->instancePtr[i] == nil)	// no instance?
						continue;

					// get a read request
					while( PBDequeueFirst( ih->readQueue, (QElemPtr *) &readRequest) == noErr)
					{
						RecordEventLogger( 'isoc', 'updt', 'disa', 'disr');

						readRequest->result = kIDHErrDeviceDisconnected;

						// call the client callback
						result = (*readRequest->completionProc)( (IDHGenericEvent *) readRequest, readRequest->refCon);
					}

					// get a read request
					while( PBDequeueFirst( ih->writeQueue, (QElemPtr *) &writeRequest) == noErr)
					{
						RecordEventLogger( 'isoc', 'updt', 'disa', 'disw');

						writeRequest->result = kIDHErrDeviceDisconnected;

						// call the client callback
						result = (*writeRequest->completionProc)( (IDHGenericEvent *) writeRequest, writeRequest->refCon);
					}					
				} // end of processing instances
				
				break;
			}
		}
	}
	
Exit:
	return result;
}
			

OSStatus writeNotifyProc( IDHGenericEvent *event, void *userData)
{
	OSErr 						result = noErr;
	IsochComponentGlobalsPtr	gGlobals = userData;
	DeviceDescription			*deviceDescriptionPtr;
	short						i;
	
	RecordEventLogger( 'isoc', 'writ', 'comp', 'lete');
	
	FailIf( event->eventHeader.event != kIDHPrivateEventWriteComplete, Exit);	// make sure this is a write
			
	deviceDescriptionPtr = nil;	// find device causing notification in list
	for( i=0; i<kMaxDevicesActive; ++i)
	{
		if( gGlobals->deviceDescription[i].deviceID == event->eventHeader.deviceID)
		{
			deviceDescriptionPtr = &gGlobals->deviceDescription[i];
			break;
		}
	}
	
	FailIf( deviceDescriptionPtr == nil, Exit);	// couldn't find device in list
	
	for( i=0; i<kMaxInstances; ++i)	// process each client for this device
	{
		IsochComponentInstancePtr	ih = deviceDescriptionPtr->instancePtr[i];
		IDHParameterBlock 			*writeRequest;
		Ptr							tmpBuffPtr;
		unsigned long				frameSize;
			
		if( deviceDescriptionPtr->instancePtr[i] == nil)	// no instance? Skip to next.
			continue;
			
		if( PBDequeueFirst( ih->writeQueue, (QElemPtr *) &writeRequest) != noErr)	// get a write request
			continue;
		
		// Get a buffer for output
		result = getEmptyOutputFrame( ih, &tmpBuffPtr, &frameSize);
		FailWithVal( result != noErr, Exit, result);
			
		writeRequest->actualCount = 0;
		writeRequest->result = noErr;

		FailMessage( frameSize < writeRequest->requestedCount)	// make sure buffer is big enough
		if( frameSize < writeRequest->requestedCount)
			result = kIDHErrDeviceWriteError;
				
		if( result == noErr)	// valid frame avail?
		{
			if( writeRequest->buffer == nil)	// user wants buffer
			{
				writeRequest->buffer = tmpBuffPtr;	// pass the buffer back to the client
				
				// call clients callback so that he can copy his data into buffer
				result = (*writeRequest->completionProc)( (IDHGenericEvent *) writeRequest, writeRequest->refCon);
				FailWithVal( result != noErr, Exit, result);
				
				// write the frame
				result = writeFrame( ih, tmpBuffPtr);
				FailWithVal( result != noErr, Exit, result);
			}
			else // client supplied frame
			{
				// move the client data into the buffer
				BlockMoveData( writeRequest->buffer, tmpBuffPtr, writeRequest->requestedCount);		
				writeRequest->actualCount = writeRequest->requestedCount;
		
				// write the frame
				result = writeFrame( ih, tmpBuffPtr);
				FailWithVal( result != noErr, Exit, result);

				// save the result and call clients callback
				writeRequest->result = result;	
				result = (*writeRequest->completionProc)( (IDHGenericEvent *) writeRequest, writeRequest->refCon);
				FailMessage( result != noErr);
			}
			
		}	// valid frame avail?
		
	}	// next client

	// reset callback
	result = IDHNotifyMeWhen( (ComponentInstance) gGlobals->localInstance, deviceDescriptionPtr->writeIsochNotificationID, 
				kIDHPrivateEventWriteComplete);
		
Exit:	
	return result;
}


OSStatus cameraNameLookup(DeviceDescriptionPtr pDeviceDescription, UInt8 *name)
{
	Handle		h;
	FSSpec		fileSpec;
	Str255		cameraName = "\pDV";
	SInt32		dirID;
	UInt32		*pGuid;
	UInt32		guidCount;
	UInt32		i, index;		
	OSStatus	result = noErr;
	SInt16		refNum;
	Boolean		foundName = false;

	BlockMoveData(cameraName, name, cameraName[0] + 1);

	// first look for a "DV Names" preferences file
	// find the preferences folder
	result = FindFolder(kOnSystemDisk, kPreferencesFolderType, kDontCreateFolder, &refNum, &dirID);
	if (result != noErr)
		return noErr;

	result = FSMakeFSSpec(refNum, dirID, "\pDV Names", &fileSpec);
	if (result == noErr)
	{
		// prefs file exsits, open it
		refNum = FSpOpenResFile(&fileSpec, fsRdPerm);
		result = ResError();
	}
	
	if (result == noErr)
	{
		// read guid resource and look for matching guid
		h = Get1Resource('guid', -20775); // jkl, get resource assignment and constant
		result = ResError();
		if (h && (result == noErr))
		{
			HLock(h);
	
			// first long is number of guid's stored in resource
			pGuid = (UInt32 *) *h;
			guidCount = *pGuid;
			pGuid++;
	
			for (i = 0, index = 1; i < (guidCount * 2); i+=2, index++)
			{
				if ((pGuid[i] == pDeviceDescription->uniqueID.hi) && (pGuid[i+1] == pDeviceDescription->uniqueID.lo))
				{
					// found device, get its name
					GetIndString(cameraName, -20775, index);
					if (cameraName[0])
					{
						BlockMoveData(cameraName, name, cameraName[0] + 1);
						foundName = true;
						break;
					}
				}	
			}
			HUnlock(h);
			ReleaseResource(h);
		}	
	
		CloseResFile(refNum);
	}
	
	if (!foundName)
	{
		// look in local vendor ID resource stored in QT FW DV Support to at least determine manufacturer name
		result = FindFolder(kOnSystemDisk, kExtensionFolderType, kDontCreateFolder, &refNum, &dirID);
		if (result != noErr)
			return noErr;
	}
	
	// jkl, this should be changed to search by type and creator, not name
	result = FSMakeFSSpec(refNum, dirID, "\pQuickTime FireWire DV Support", &fileSpec);
	if (result == noErr)
	{
		// prefs file exsits, open it
		refNum = FSpOpenResFile(&fileSpec, fsRdPerm);
		result = ResError();
		if (result != noErr)
			return noErr;
	}
	
	if (result == noErr)
	{
		// read vendor id resource and look for matching guid
		h = Get1Resource('vnid', -20775); // jkl, get resource assignment and constant
		result = ResError();
		if (h && (result == noErr))
		{
			HLock(h);
	
			// first long is number of vendor id's stored in resource
			guidCount = **((UInt32 **) h);
	
			pGuid = *((UInt32 **) h);
			pGuid++;
	
			for (i = 0, index = 1; i < guidCount; i++, index++)
			{
				if (pGuid[i] == (pDeviceDescription->uniqueID.hi >> 8))
				{
					// found device, get its name
					GetIndString(cameraName, -20775, index);
					if (cameraName[0])
					{
						BlockMoveData(cameraName, name, cameraName[0] + 1);
						BlockMoveData(" DV", name + name[0] + 1, 3);
						name[0] += 3;
						break;
					}
				}	
			}
			HUnlock(h);
			ReleaseResource(h);
		}	
	
		CloseResFile(refNum);
	}

	// don't fail because can't find a name
	return noErr;
}

	
struct UniqueIDAndName {
	CSRNodeUniqueID		guid;
	Str255				name;
};
typedef struct UniqueIDAndName UniqueIDAndName, *UniqueIDAndNamePtr;

// structural representation of preferences file data
struct CameraNamePrefs {
	UInt32				idNameCount;
	UniqueIDAndName		idName[];
};
typedef struct CameraNamePrefs CameraNamePrefs, *CameraNamePrefsPtr;

OSStatus updateCameraName(CSRNodeUniqueID *guid, UInt8 *name)
{
	UniqueIDAndNamePtr	pGuidName;
	FSSpec		fileSpec;
	SInt32		dirID;
	SInt32		requestedCount;
	UInt32		recordCount;
	UInt32		i;		
	OSStatus	result = noErr;
	SInt16		refNum, vRefNum;
	Boolean		foundGuid = false;
	Boolean		fileOpened = false;

	// look for a "DV Names" preferences file, jkl - name needs to be localized
	// find the preferences folder
	result = FindFolder(kOnSystemDisk, kPreferencesFolderType, kDontCreateFolder, &vRefNum, &dirID);
	if (result != noErr)
		return noErr;

	result = FSMakeFSSpec(vRefNum, dirID, "\pDV Names", &fileSpec);
	if (result == fnfErr)
		result = FSpCreate(&fileSpec, 'dvcf', 'pref', smSystemScript);
	
	if (result == noErr)
		result = FSpOpenDF(&fileSpec, fsRdWrPerm, &refNum);
	
	if (result == noErr)
	{
		fileOpened = true;
		result = SetFPos(refNum, fsFromStart, 0);
	}
		
	if (result == noErr)
	{
		requestedCount = 4;
		result = FSRead(refNum, &requestedCount, &recordCount);
	}
	
	if ((result == noErr) && (requestedCount == 4))
	{
		requestedCount = recordCount * sizeof(UniqueIDAndName);
		
		// allocate memory to read all of the data
		pGuidName = (UniqueIDAndNamePtr) NewPtr(requestedCount);
		if (pGuidName == nil)
			result = memFullErr;
			
		if (result == noErr)
			result = FSRead(refNum, &requestedCount, pGuidName);
			
		if (result == noErr)
		{
			for (i = 0; i < recordCount; i++)
			{
				if ((pGuidName[i].guid.hi == guid->hi) && (pGuidName[i].guid.lo == guid->lo))
				{
					BlockMoveData(name, pGuidName[i].name, name[0] + 1);
					foundGuid = true;
					break;
				}
			}
		}
	}
	else
	{
		// prefs file is empty		
		result = noErr;
		recordCount = 1;
		requestedCount = sizeof(UniqueIDAndName);

		// allocate memory for new record
		pGuidName = (UniqueIDAndNamePtr) NewPtr(requestedCount);
		if (pGuidName == nil)
			result = memFullErr;

		if (result == noErr)
		{
			BlockMoveData(name, pGuidName[0].name, name[0] + 1);
			pGuidName[0].guid = *guid;
			foundGuid = true;
		}
	}
	
	if ((result == noErr) && !foundGuid)
	{
		// allocate space for new record
		requestedCount = GetPtrSize((Ptr) pGuidName);
		requestedCount += sizeof(UniqueIDAndName);
		SetPtrSize((Ptr) pGuidName, requestedCount);
		result = MemError();
		
		if (result == noErr)
		{
			BlockMoveData(name, pGuidName[recordCount].name, name[0] + 1);
			pGuidName[recordCount].guid = *guid;
		}
		recordCount++;
	}
	
	if (result == noErr)
	{
		// write data back to disk
		result = SetFPos(refNum, fsFromStart, 0);

		requestedCount = 4;
		if (result == noErr)
			result = FSWrite(refNum, &requestedCount, &recordCount);
			
		requestedCount = GetPtrSize((Ptr) pGuidName);
		if (result == noErr)
			result = FSWrite(refNum, &requestedCount, pGuidName);
	}
	
	if (fileOpened)
		FSClose(refNum);
		
	FlushVol(nil, vRefNum);
			
	return result;
}
	
#pragma mark -
#pragma mark ееееееееее Firewire Calls  ееееееееее

OSStatus handleDeviceAdded(ComponentInstance ihc, GDFDeviceEventData *pGDFData)
{
	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
	IDHDeviceID					deviceID;
	OSStatus					status = noErr;

	RecordEventLogger( 'isoc', 'hana', 0, 0); //еее
	
	// update component info
	status = updateDeviceList(ih, pGDFData, &deviceID, true /* activate */);
	
	// Post deviceAddedEvent to anybody that wants one
	postEvent(ih->gGlobals, deviceID, kIDHEventDeviceAdded, nil);
	
	return status;
}


OSStatus handleDeviceRemoved(ComponentInstance ihc, GDFDeviceEventData *pGDFData)
{
	IsochComponentInstancePtr 	ih = (IsochComponentInstancePtr) ihc;
	IDHDeviceID					deviceID;
	OSStatus					status = noErr;

	RecordEventLogger( 'isoc', 'hanr', 0, 0);

	// update component info
	status = updateDeviceList(ih, pGDFData, &deviceID, false /* deactivate */);
	
	// Post deviceAddedEvent to anybody that wants one
	postEvent(ih->gGlobals, deviceID, kIDHEventDeviceRemoved, nil);
	
	return status;
}


OSStatus initIsochPort(FWClientInitIsochPortParams *pInitIsochPortParams, UInt32* outCommandAcceptance)
{
	DeviceDescriptionPtr		pDeviceDescription;
	OSErr						error = noErr;
	Boolean						portIsTalker, trial;
	Boolean						portIsLocal = false;
	
	pDeviceDescription = (DeviceDescriptionPtr) pInitIsochPortParams->fwClientIsochPortParams.refCon;
	
	// Get port information
	portIsTalker = pInitIsochPortParams->fwClientIsochPortParams.portIsTalker;
	trial = pInitIsochPortParams->trial;

	// if input and call is not for talker, then call is for local port
	if ((pDeviceDescription->inputIsochChannelID == pInitIsochPortParams->fwClientIsochPortParams.isochChannelID) && !portIsTalker)
		portIsLocal = true;
	// JKL, really could be an else here
	// if output and call is for talker, then call is for local port
	if ((pDeviceDescription->outputIsochChannelID == pInitIsochPortParams->fwClientIsochPortParams.isochChannelID) && portIsTalker)
		portIsLocal = true;

	if (portIsTalker)
	{
		if (!trial)
		{
			if (portIsLocal)
				error = doLocalTalkerPort(pDeviceDescription, pInitIsochPortParams);
			else
				error = doRemoteTalkerPort(pDeviceDescription, pInitIsochPortParams);
		}
		else
		{
			if (portIsLocal)
				error = trialLocalTalkerPort(pDeviceDescription, pInitIsochPortParams);
			else
				error = trialRemoteTalkerPort(pDeviceDescription, pInitIsochPortParams);
		}
	}
	else
	{
		if (!trial)
		{
			if (portIsLocal)
				error = doLocalListenerPort(pDeviceDescription, pInitIsochPortParams);
			else
				error = doRemoteListenerPort(pDeviceDescription, pInitIsochPortParams);
		}
		else
		{
			if (portIsLocal)
				error = trialLocalListenerPort(pDeviceDescription, pInitIsochPortParams);
			else
				error = trialRemoteListenerPort(pDeviceDescription, pInitIsochPortParams);
		}
	}
	
	FailMessageVal( error != noErr, error);

	FWClientCommandIsComplete(
			pInitIsochPortParams->fwClientInterfaceParams.fwClientCommandID,
			error);
			
	*outCommandAcceptance = kFWClientCommandAcceptNoMore;

	return error;
}


OSStatus doLocalTalkerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams)
{
	OSStatus error = noErr;
	
	error = FWAllocateIsochPortID(&pDeviceDescription->isochPortID, pDeviceDescription->dclProgramID,
								  pInitIsochPortParams->channelNum, pInitIsochPortParams->speed, true);
	FailMessageVal( error != noErr, error);

	// Send an allocate isoch port command to allocate port for talking
	if (error == noErr)
	{
		FWSetFWCommandParams(pDeviceDescription->isochPortCommandObjectID, pDeviceDescription->fwClientID, kFWCommandSyncFlag, nil, 0);
		FWSetIsochPortCommandIsochPortID(pDeviceDescription->isochPortCommandObjectID, pDeviceDescription->isochPortID);
		error = FWAllocateLocalIsochronousPort(pDeviceDescription->isochPortCommandObjectID);
		FailMessageVal( error != noErr, error);
	}

	if (pDeviceDescription->componentGlobals->useCMP)
	{
		if (error == noErr)
			error = cmpNewPointToPointConnection(4 * pDeviceDescription->pcrIndex,
												 pDeviceDescription->localFWReferenceID,
												 pInitIsochPortParams->channelNum,
												 pInitIsochPortParams->speed,				
												 pDeviceDescription->cmpOverheadID,
												 pDeviceDescription);				
	}
	
	return error;
}			


OSStatus doRemoteTalkerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams)
{
	OSStatus error = noErr;

	if (pDeviceDescription->componentGlobals->useCMP)
	{
		// set camera's output plug
		if (error == noErr)
			error = cmpNewPointToPointConnection(0x04, pDeviceDescription->fwClientID, pInitIsochPortParams->channelNum,
												 pInitIsochPortParams->speed, pDeviceDescription->cmpOverheadID, pDeviceDescription);
	}

	return error;
}
				

OSStatus trialLocalTalkerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams)
{
	UInt32		localOutputPCRValue, pcrValue;
	UInt32		fwBandwidthUnits;
	UInt32		channelNum;
	OSStatus	error = noErr;

	if (pDeviceDescription->componentGlobals->useCMP)
	{
		error = cmpRead(4 * pDeviceDescription->pcrIndex, pDeviceDescription->localFWReferenceID, &pcrValue, pDeviceDescription);
		
		if (error == noErr)
		{
			// initialize mac output plug, turns on connected bit, 448 overhead unlike cameras so two streams fit in started 0x1333 bandwidth, 122 quadlet payload
			// jkl, need to computer overhead, ask Eric
			pDeviceDescription->cmpOverheadID = 0x0c;
			localOutputPCRValue = 0x8000007a | (pDeviceDescription->cmpOverheadID << 10);
			error = cmpWrite(4 * pDeviceDescription->pcrIndex, pDeviceDescription->localFWReferenceID,
							 pcrValue, localOutputPCRValue, pDeviceDescription);
		}
	
		if (error == noErr)
		{
			// reserve bandwidth
			// 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
			// could either stay at default s100 or fix up the bandwidth in the commit phase
			fwBandwidthUnits = irmCalculateBandwidthUnits(localOutputPCRValue);
			error = irmAllocateBandwidth(pDeviceDescription->fwClientID, fwBandwidthUnits, pDeviceDescription);
		}
		
		// reserve channel
		if (error == noErr)
		{
			pDeviceDescription->cmpBandwidth = fwBandwidthUnits;
			
			channelNum = 0;
	
			// reserve channel
			// jkl, what if further init stuff fails - like target device in camera mode, how do we know to release resources
			error = irmAllocateChannel(pDeviceDescription->fwClientID, &channelNum, pDeviceDescription);
			
			if (error == noErr)
			{
				pDeviceDescription->cmpChannel = channelNum;

				if (channelNum < 32)
				{
					pInitIsochPortParams->supportedChannelNumHi = ((UInt32) 0x80000000) >> channelNum;
					pInitIsochPortParams->supportedChannelNumLo = 0;
				}
				else
				{
					channelNum -= 32;
					pInitIsochPortParams->supportedChannelNumHi = 0;
					pInitIsochPortParams->supportedChannelNumLo = ((UInt32) 0x80000000) >> channelNum;
				}
			}
		}

		// set speed
		if (error == noErr)
		{
			UInt32		localOutputMasterPlugValue;
			
			error = cmpRead(0x00, pDeviceDescription->localFWReferenceID, &localOutputMasterPlugValue, pDeviceDescription);
			if (error == noErr)
				pInitIsochPortParams->speed = (localOutputMasterPlugValue >> 30) & 3;
			else
			{
				pInitIsochPortParams->speed = kFWSpeed100MBit;
				
				// don't fail for this
				error = noErr;
			}
		}
	
	}
	else
	{
		// no CMP just use channel 63, should make sure bandwidth is allocated
		pInitIsochPortParams->supportedChannelNumHi = 0;
		pInitIsochPortParams->supportedChannelNumLo = 1;
		
		// set speed
		pInitIsochPortParams->speed = kFWSpeed100MBit;
	}
	
	return error;
}
				

OSStatus trialRemoteTalkerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams)
{
	UInt32		fwBandwidthUnits;
	UInt32		channelNum;
	UInt32		pcrValue;
	OSStatus	error = noErr;

	if (pDeviceDescription->componentGlobals->useCMP)
	{
		// determine current state of device
		error = cmpRead(0x04, pDeviceDescription->fwClientID, &pcrValue, pDeviceDescription);
		if (error == noErr)
		{
			// save channelNum
			channelNum = (pcrValue >> 16) & 0x3f;
					
			// if there is a point to point connection or if camera is broadcasting on a channel other than 63,
			// use existing channel and bandwidth
			if ((pcrValue & 0x3f000000) || (((pcrValue & 0xC0000000) == 0xC0000000) && (channelNum != 63)))
			{
				if (channelNum < 32)
				{
					pInitIsochPortParams->supportedChannelNumHi = ((UInt32) 0x80000000) >> channelNum;
					pInitIsochPortParams->supportedChannelNumLo = 0;
				}
				else
				{
					channelNum -= 32;
					pInitIsochPortParams->supportedChannelNumHi = 0;
					pInitIsochPortParams->supportedChannelNumLo = ((UInt32) 0x80000000) >> channelNum;
				}
				
				pDeviceDescription->cmpChannel = channelNum;
				pDeviceDescription->cmpBandwidth = irmCalculateBandwidthUnits(pcrValue);
				pDeviceDescription->cmpOverheadID = (pcrValue >> 10) & 0x0f;
			}
			else
			{
				if ((pcrValue & 0xC0000000) == 0xC0000000)
				{
					// device is broadcasting on 63, clear broadcast, release channel and bandwidth
					error = cmpWrite(0x04, pDeviceDescription->fwClientID, pcrValue, pcrValue & 0xbfffffff, pDeviceDescription);
					if (error == noErr)
						error = irmReleaseChannel(pDeviceDescription->fwClientID, 63, pDeviceDescription);
					if (error == noErr)
					{
						fwBandwidthUnits = irmCalculateBandwidthUnits(pcrValue);
						error = irmReleaseBandwidth(pDeviceDescription->fwClientID, fwBandwidthUnits, pDeviceDescription);
					}
				}

				// allocate bandwidth
				// jkl, bandwidth could be wrong if speed is negotiated faster than s100, too much is allocated and not enough gets deallocated
				// could just hard wire to s100 or re-check bandwith in commit phase
				pDeviceDescription->cmpOverheadID = 0x0c;
				fwBandwidthUnits = irmCalculateBandwidthUnits((pcrValue & 0x03ff) | (pDeviceDescription->cmpOverheadID << 10));
				error = irmAllocateBandwidth(pDeviceDescription->fwClientID, fwBandwidthUnits, pDeviceDescription);

				if (error == noErr)
				{
					pDeviceDescription->cmpBandwidth = fwBandwidthUnits;
					
					// allocate channel
					channelNum = 0;
					error = irmAllocateChannel(pDeviceDescription->fwClientID, &channelNum, pDeviceDescription);				
					if (error == noErr)
					{
						pDeviceDescription->cmpChannel = channelNum;
						
						if (channelNum < 32)
						{
							pInitIsochPortParams->supportedChannelNumHi = ((UInt32) 0x80000000) >> channelNum;
							pInitIsochPortParams->supportedChannelNumLo = 0;
						}
						else
						{
							channelNum -= 32;
							pInitIsochPortParams->supportedChannelNumHi = 0;
							pInitIsochPortParams->supportedChannelNumLo = ((UInt32) 0x80000000) >> channelNum;
						}
					}
				}
			}
		}

		// set speed
		if (error == noErr)
		{
			UInt32		remoteOutputMasterPlugValue;
			
			error = cmpRead(0x00, pDeviceDescription->fwClientID, &remoteOutputMasterPlugValue, pDeviceDescription);
			if (error == noErr)
				pInitIsochPortParams->speed = (remoteOutputMasterPlugValue >> 30) & 3;
			else
			{
				pInitIsochPortParams->speed = kFWSpeed100MBit;
				
				// don't fail for this
				error = noErr;
			}
		}
		
	}
	else
	{
		// just use channel 63
		pInitIsochPortParams->supportedChannelNumHi = 0;
		pInitIsochPortParams->supportedChannelNumLo = 1;

		// set speed
		pInitIsochPortParams->speed = kFWSpeed100MBit;
	}
	
	return error;
}
				

OSStatus doLocalListenerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams)
{
	OSStatus error = noErr;
	
	error = FWAllocateIsochPortID(&pDeviceDescription->isochPortID, pDeviceDescription->dclProgramID,
								  pInitIsochPortParams->channelNum, pInitIsochPortParams->speed, false);
	FailMessageVal( error != noErr, error);

	// Send an allocate isoch port command to allocate port for listening
	if (error == noErr)
	{	
		FWSetFWCommandParams(pDeviceDescription->isochPortCommandObjectID, pDeviceDescription->fwClientID, kFWCommandSyncFlag, nil, 0);
		FWSetIsochPortCommandIsochPortID(pDeviceDescription->isochPortCommandObjectID, pDeviceDescription->isochPortID);
		error = FWAllocateLocalIsochronousPort(pDeviceDescription->isochPortCommandObjectID);
		FailMessageVal( error != noErr, error);
	}

	if (pDeviceDescription->componentGlobals->useCMP)
	{
		// set mac's input plug
		if (error == noErr)
			error = cmpNewPointToPointConnection(0x80 + (4 * pDeviceDescription->pcrIndex),
												 pDeviceDescription->localFWReferenceID,
												 pInitIsochPortParams->channelNum, 0, 0, pDeviceDescription);				
	}				

	return error;
}
				

OSStatus doRemoteListenerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams)
{
	OSStatus error = noErr;

	if (pDeviceDescription->componentGlobals->useCMP)
	{
		if (error == noErr)
		{
			// set camera's input plug
			error = cmpNewPointToPointConnection(0x84, pDeviceDescription->fwClientID, pInitIsochPortParams->channelNum, 0, 0, pDeviceDescription);
		}
	}

	return error;
}
				

OSStatus trialLocalListenerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams)
{
	UInt32		pcrValue;
	OSStatus	error = noErr;

	if (pDeviceDescription->componentGlobals->useCMP)
	{
		// initialize mac plug, on-line, read value should be 0
		error = cmpRead(4 * pDeviceDescription->pcrIndex, pDeviceDescription->localFWReferenceID, &pcrValue, pDeviceDescription);
		if (error == noErr)
			error = cmpWrite(4 * pDeviceDescription->pcrIndex, pDeviceDescription->localFWReferenceID,
							 pcrValue, 0x80000000, pDeviceDescription);

		if (error == noErr)
			error = cmpRead(0x80, pDeviceDescription->localFWReferenceID, &pcrValue, pDeviceDescription);

		if (error == noErr)
			pInitIsochPortParams->speed = (pcrValue >> 30) & 3;
		else
		{
			pInitIsochPortParams->speed = kFWSpeed100MBit;
			
			// don't fail for this
			error = noErr;
		}
	}
	else
		pInitIsochPortParams->speed = kFWSpeed100MBit;
		
	pInitIsochPortParams->supportedChannelNumHi = 0xffffffff;
	pInitIsochPortParams->supportedChannelNumLo = 0xffffffff;

	return error;
}
				

OSStatus trialRemoteListenerPort(DeviceDescriptionPtr pDeviceDescription, FWClientInitIsochPortParams *pInitIsochPortParams)
{
	UInt32		remoteInputMasterPlugValue;
	OSStatus	error = noErr;

	if (pDeviceDescription->componentGlobals->useCMP)
	{
		error = cmpRead(0x80, pDeviceDescription->fwClientID, &remoteInputMasterPlugValue, pDeviceDescription);
		if (error == noErr)
			pInitIsochPortParams->speed = (remoteInputMasterPlugValue >> 30) & 3;
		else
		{
			pInitIsochPortParams->speed = kFWSpeed100MBit;
			
			// don't fail for this
			error = noErr;
		}
	}
	else
		pInitIsochPortParams->speed = kFWSpeed100MBit;

	pInitIsochPortParams->supportedChannelNumHi = 0xffffffff;
	pInitIsochPortParams->supportedChannelNumLo = 0xffffffff;

	return error;
}
				

OSStatus releaseIsochPort(FWClientReleaseIsochPortParams *pReleaseIsochPortParams, UInt32* outCommandAcceptance)
{
	DeviceDescriptionPtr		pDeviceDescription;
	FWReferenceID				fwClientID;
	Boolean						portIsTalker;
	Boolean						portIsLocal = false;
	OSErr						error = noErr;

	pDeviceDescription = (DeviceDescriptionPtr) pReleaseIsochPortParams->fwClientIsochPortParams.refCon;

	// Get port information
	fwClientID = pReleaseIsochPortParams->fwClientInterfaceParams.fwReferenceID;
	portIsTalker = pReleaseIsochPortParams->fwClientIsochPortParams.portIsTalker;

	// if input and call is not for talker, then call is for local port
	if ((pDeviceDescription->inputIsochChannelID == pReleaseIsochPortParams->fwClientIsochPortParams.isochChannelID) && !portIsTalker)
		portIsLocal = true;
	// JKL, really could be an else here
	// if output and call is for talker, then call is for local port
	if ((pDeviceDescription->outputIsochChannelID == pReleaseIsochPortParams->fwClientIsochPortParams.isochChannelID) && portIsTalker)
		portIsLocal = true;

	// if called for local port, release port from FSL
	if (portIsLocal)
	{
		FWSetFWCommandParams(pDeviceDescription->isochPortCommandObjectID, pDeviceDescription->fwClientID, kFWCommandSyncFlag, nil, 0);
		FWSetIsochPortCommandIsochPortID(pDeviceDescription->isochPortCommandObjectID, pDeviceDescription->isochPortID);
		FWReleaseLocalIsochronousPort(pDeviceDescription->isochPortCommandObjectID);

		FWDeallocateIsochPortID(pDeviceDescription->isochPortID);
		pDeviceDescription->isochPortID = 0;
	}

	if (pDeviceDescription->componentGlobals->useCMP)
	{
		if (portIsTalker)
		{
			if (portIsLocal)
				error = cmpDisposePointToPointConnection(pDeviceDescription->pcrIndex * 4, pDeviceDescription->localFWReferenceID, pDeviceDescription);
			else
				error = cmpDisposePointToPointConnection(0x04, fwClientID, pDeviceDescription);
		}
		else
		{
			if (portIsLocal)
				error = cmpDisposePointToPointConnection(0x80 + (pDeviceDescription->pcrIndex * 4), pDeviceDescription->localFWReferenceID, pDeviceDescription);
			else
				error = cmpDisposePointToPointConnection(0x84, fwClientID, pDeviceDescription);
		}
	}

	FWClientCommandIsComplete(
			pReleaseIsochPortParams->fwClientInterfaceParams.fwClientCommandID,
			error);

	*outCommandAcceptance = kFWClientCommandAcceptNoMore;

	return error;
}


OSStatus startIsochPort(FWClientIsochPortControlParams *pIsochPortControlParams, UInt32* outCommandAcceptance)
{	
	DeviceDescriptionPtr		pDeviceDescription;
	FWClientCommandID			fwClientCommandID;
	FWReferenceID				fwClientID;
	Boolean						completionIsPending = false;
	Boolean						portIsTalker;
	Boolean						portIsLocal = false;
	OSErr						error = noErr;

	RecordEventLogger( 'isoc', 'strt', 'port', 'bgn ');

	pDeviceDescription = (DeviceDescriptionPtr) pIsochPortControlParams->fwClientIsochPortParams.refCon;

	// Get port information
	fwClientID = pIsochPortControlParams->fwClientInterfaceParams.fwReferenceID;
	portIsTalker = pIsochPortControlParams->fwClientIsochPortParams.portIsTalker;

	// if input and call is not for talker, then call is for local port
	if ((pDeviceDescription->inputIsochChannelID == pIsochPortControlParams->fwClientIsochPortParams.isochChannelID) && !portIsTalker)
		portIsLocal = true;
	// JKL, really could be an else here
	// if output and call is for talker, then call is for local port
	if ((pDeviceDescription->outputIsochChannelID == pIsochPortControlParams->fwClientIsochPortParams.isochChannelID) && portIsTalker)
		portIsLocal = true;

	// if called for local port, start port
	if (portIsLocal)
	{
		fwClientCommandID = pIsochPortControlParams->fwClientInterfaceParams.fwClientCommandID;
		FWSetFWCommandParams(pDeviceDescription->startIsochPortCommandObjectID, pDeviceDescription->fwClientID, 0, isochPortCommandCompletionProc, (UInt32) fwClientCommandID);
		FWSetIsochPortCommandIsochPortID(pDeviceDescription->startIsochPortCommandObjectID, pDeviceDescription->isochPortID);
		error = FWStartLocalIsochronousPort(pDeviceDescription->startIsochPortCommandObjectID);
		FailMessageVal( error != noErr, error);
		completionIsPending = true; //zzz what if above call returns an error?
	}

	if ((error == noErr) && completionIsPending)
		error = kIDHErrCompletionPending;
		
	if (error != kIDHErrCompletionPending)
	{
		FWClientCommandIsComplete(
				pIsochPortControlParams->fwClientInterfaceParams.fwClientCommandID,
				error);
	}
	else
	{
		error = noErr;
	}
		
	*outCommandAcceptance = kFWClientCommandAcceptNoMore;
	
	return error;
}


OSStatus stopIsochPort(FWClientIsochPortControlParams *pIsochPortControlParams, UInt32* outCommandAcceptance)
{
	DeviceDescriptionPtr		pDeviceDescription;
	FWClientCommandID			fwClientCommandID;
	FWReferenceID				fwClientID;
	Boolean						completionIsPending = false;
	Boolean						portIsTalker;
	Boolean						portIsLocal = false;
	OSErr						error = noErr;

	pDeviceDescription = (DeviceDescriptionPtr) pIsochPortControlParams->fwClientIsochPortParams.refCon;
	
	// Get port information
	fwClientID = pIsochPortControlParams->fwClientInterfaceParams.fwReferenceID;
	portIsTalker = pIsochPortControlParams->fwClientIsochPortParams.portIsTalker;

	// if input and call is not for talker, then call is for local port
	if ((pDeviceDescription->inputIsochChannelID == pIsochPortControlParams->fwClientIsochPortParams.isochChannelID) && !portIsTalker)
		portIsLocal = true;
	// JKL, really could be an else here
	// if output and call is for talker, then call is for local port
	if ((pDeviceDescription->outputIsochChannelID == pIsochPortControlParams->fwClientIsochPortParams.isochChannelID) && portIsTalker)
		portIsLocal = true;

	// if called for local port, stop port
	if (portIsLocal)
	{
		fwClientCommandID = pIsochPortControlParams->fwClientInterfaceParams.fwClientCommandID;
		FWSetFWCommandParams(pDeviceDescription->stopIsochPortCommandObjectID, pDeviceDescription->fwClientID, 0, isochPortCommandCompletionProc, (UInt32) fwClientCommandID);
		FWSetIsochPortCommandIsochPortID(pDeviceDescription->stopIsochPortCommandObjectID, pDeviceDescription->isochPortID);
		error = FWStopLocalIsochronousPort(pDeviceDescription->stopIsochPortCommandObjectID);
		FailMessageVal( error != noErr, error);
		completionIsPending = true; //zzz what if above call returns an error?
	}

	if ((error == noErr) && completionIsPending)
		error = kIDHErrCompletionPending;
		
	if (error != kIDHErrCompletionPending)
	{
		FWClientCommandIsComplete(
				pIsochPortControlParams->fwClientInterfaceParams.fwClientCommandID,
				error);
	}
	else
	{
		error = noErr;
	}

	*outCommandAcceptance = kFWClientCommandAcceptNoMore;

	return error;
}


void isochPortCommandCompletionProc(FWCommandObjectID fwCommandObjectID, OSStatus commandStatus, UInt32 completionProcData)
{
	FWClientCommandIsComplete((FWClientCommandID) completionProcData, commandStatus);
}

////////////////////////////////////////////////////////////////////////////////
//
// cmpDisposePointToPointConnection
//
// Tear down a point to point connection through the client's plug control register.
//
// *** Must be task level only
OSErr cmpDisposePointToPointConnection (
	UInt32						pcrOffset,			// offset of register address from fffff0000900 
	FWClientID					clientID,
	DeviceDescriptionPtr		pDeviceDescription)
{
	UInt32						pcrValue, expectedPCRValue;
	OSErr						err = noErr;
	
	if (pcrOffset > 0x00fc)
		err = addressRangeErr;
	
	// get existing plug control register value
	if (err == noErr)
		err = cmpRead(pcrOffset, clientID, &pcrValue, pDeviceDescription);
	
	if (err == noErr)
	{
		expectedPCRValue = pcrValue;
		
		// decrement point to point counter
		// jkl, only if it is already set, need to fix this
		if (pcrValue & 0x3f000000)
			pcrValue -= 0x01000000;
		
		// if point to point goes to zero, also turn off broadcast
		// jkl, Sony suggests to also make sure camera is stopped
		if (!(pcrValue & 0x3f000000))
			pcrValue &= 0xbfffffff;
		
		err = cmpWrite(pcrOffset, clientID, expectedPCRValue, pcrValue, pDeviceDescription);
		
		// release isochronous resources if counter goes to zero and device was talking
		if (err == noErr)
		{
			if (!(pcrValue & 0x3f000000) && (pcrOffset < 0x080))
			{
				irmReleaseChannel(clientID, (pcrValue & 0x003f0000) >> 16, pDeviceDescription);
				irmReleaseBandwidth(clientID, irmCalculateBandwidthUnits(pcrValue), pDeviceDescription);
			}
		}		
	}

	return err;
}


////////////////////////////////////////////////////////////////////////////////
//
// cmpNewPointToPointConnection
//
// Create a point to point connection through the client's and the local
// plug control register.
//
// *** Must be task level only
OSErr cmpNewPointToPointConnection (
	UInt32						pcrOffset,			// offset of register address from fffff0000900 
	FWClientID					clientID,
	UInt32						channel,
	UInt32						speed,
	UInt32						overhead,
	DeviceDescriptionPtr		pDeviceDescription)
{
	UInt32						pcrValue, expectedPCRValue;
	OSErr						err = noErr;
	
	if (pcrOffset > 0x00fc)
		err = addressRangeErr;
	
	// get existing plug control register value
	if (err == noErr)
		err = cmpRead(pcrOffset, clientID, &pcrValue, pDeviceDescription);
	
	if (err == noErr)
	{
		expectedPCRValue = pcrValue;
		
		// increment point to point counter, will this ever overflow?, not turning off broadcast
		pcrValue += 0x01000000;

		if (pcrOffset < 0x80)
		{// setting up output plug
			// set speed
			pcrValue |= (speed << 14);

			// set overhead
			pcrValue |= (overhead << 10);

			// if not already broadcasting or if point-to-point counter was previously not 0, set channel
			if (((pcrValue & 0x3f000000) == 0x01000000) && !(pcrValue & 0x40000000))
			{
				// set channel number
				pcrValue &= 0xffc0ffff;
				pcrValue |= (channel << 16);
			}
		}
		else
		{
			// for input plug just set channel if no point-to-point exists
			if ((pcrValue & 0x3f000000) == 0x01000000)
			{
				// set channel number
				pcrValue &= 0xffc0ffff;
				pcrValue |= (channel << 16);
			}
		}
	
		err = cmpWrite(pcrOffset, clientID, expectedPCRValue, pcrValue, pDeviceDescription);
	}

	return err;
}
	
		
////////////////////////////////////////////////////////////////////////////////
//
// cmpRead
//
// Read the plug control register specified by the offset.
//
// *** Must be task level only
OSErr cmpRead (
	UInt32						pcrOffset,			// offset of register address from fffff0000900 
	FWClientID					clientID,
	UInt32						*pcrValue,
	DeviceDescriptionPtr		deviceDescriptionPtr)
{
	FWCommandObjectID			fwCommandObjectID = nil;
	UInt32						retryCount;
	OSErr						err = noErr;

	if (pcrOffset > 0x00fc)
		err = addressRangeErr;
	
	fwCommandObjectID = deviceDescriptionPtr->asynchCommandObjectID;
	
	// set command params for synchronous read
	if (err == noErr)
		err = FWSetFWCommandParams(fwCommandObjectID, clientID, kFWCommandSyncFlag, nil, 0);
	
	// set more command params for synchronous read
	if (err == noErr)
		err = FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000900 + pcrOffset, (Ptr) pcrValue, 4);
	
	if (err == noErr)
	{
		retryCount = 10;
		do
		{
			retryCount--;
			err = FWRead(fwCommandObjectID);
			if (err == noErr)
				return err;
			
			// don't slam the camera too hard
			DelayForHardware(DurationToAbsolute(1 * durationMillisecond));
		}
		// probably should retry only for specific error like retryExceeded or timeOut
		while (err && retryCount);
	
		err = retryExceededErr;
	}
	
	return err;
}
	
	
////////////////////////////////////////////////////////////////////////////////
//
// cmpWrite
//
// Write a plug control register through a compare and swap lock transaction.
// The plug control register is specified by the offset. After the transaction
// The expectedPCRValue is set to the value of the PCR prior before the request.
// If this value equals the original passed in value the command completed.
//
// *** Must be task level only

OSErr cmpWrite (
	UInt32						pcrOffset,			// offset of register address from fffff0000900 
	FWClientID					clientID,
	UInt32						expectedPCRValue,
	UInt32						newPCRValue,
	DeviceDescriptionPtr		deviceDescriptionPtr)
{
	FWCommandObjectID			fwCommandObjectID = nil;
	UInt32						compareAndSwapBuffer[2];
	UInt32						retryCount;
	OSErr						err = noErr;

	if (pcrOffset > 0x00fc)
		err = addressRangeErr;
		
	fwCommandObjectID = deviceDescriptionPtr->asynchCommandObjectID;
	
	// set command params for synchronous lock
	if (err == noErr)
		err = FWSetFWCommandParams(fwCommandObjectID, clientID, kFWCommandSyncFlag, nil, 0);
	
	// set more command params for synchronous lock
	if (err == noErr)
	{
		compareAndSwapBuffer[0] = expectedPCRValue;
		compareAndSwapBuffer[1] = newPCRValue;
		err = FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000900 + pcrOffset, (Ptr) compareAndSwapBuffer, 8);
	}
	
	retryCount = 10;
	do
	{
		retryCount--;
		// JKL, fix this, arguments need to be fixed up after each try
		err = FWCompareAndSwap(fwCommandObjectID);
		if (err == noErr)
		{
			if (compareAndSwapBuffer[0] == expectedPCRValue)
				return err;
			else
			{
				// force it to try again, although probably lost cause at this point
				// jkl, need to start back at the beginning of the write command to redo values
				err = retryExceededErr;
				
				// jkl ***, hack, reset expectedPCRValue to what was returned even though this no longer may be a valid starting argument
				expectedPCRValue = compareAndSwapBuffer[0];
			}
		}
		
		// don't slam the camera too hard
		DelayForHardware(DurationToAbsolute(1 * durationMillisecond));
	}
	// probably should retry only for specific error like retryExceeded or timeOut
	while (err && retryCount);
	
	return err;
}


////////////////////////////////////////////////////////////////////////////////
//
// cmpHandleLocalLock
//
// This is a notification routine that is called once the response to a lock
// transaction to one of the PCR addresses has been sent. One way to handle
// this is to look at the result of the transaction, is it consistent with
// the current configuration. Does the configuration change to match the PCR
// request or is the PCR updated with the current configuration overriding
// the lock request. This may also be called for locks initiated by the driver
// to a local PCR.
//
// There is also a LockRequest notification that could be setup to look at the
// request. I don't know if we have the ability to override the lock request
// and basically deny it before FSL responds. Something to talk over with Eric
// and Collin.
//
OSStatus cmpHandleLocalLock(
	FWClientAsynchRequestParamsPtr	pFWClientAsynchRequestParams,
	UInt32							*pCommandAcceptance)
{
	DeviceDescriptionPtr pDeviceDescription;
	OSStatus status = noErr;
	
	pDeviceDescription = (DeviceDescriptionPtr) pFWClientAsynchRequestParams->pAddressSpecificData;

	// now what?
	
	// Complete FireWire client command.
	FWClientCommandIsComplete(pFWClientAsynchRequestParams->fwClientInterfaceParams.fwClientCommandID, status);
	*pCommandAcceptance = kFWClientCommandAcceptNoMore;

	return status;
}


// *** Must be task level only
OSStatus irmReleaseChannel(
	FWClientID					fwClientID,
	UInt32						channel,
	DeviceDescriptionPtr		deviceDescriptionPtr)
{
	FWReferenceID				fwIsochResourceManagerID;
	FWCommandObjectID			fwCommandObjectID = nil;
	UInt32						asynchCommandArgs[2];
	UInt32						clippedChannel, saveMaxRT;
	OSStatus					status = noErr;
	
	status = FWGetFWIsochResourceManagerID(fwClientID, &fwIsochResourceManagerID);
	
	if ((status == noErr) && (fwIsochResourceManagerID != (FWReferenceID) kInvalidFWIsochResourceManagerID))
	{
		fwCommandObjectID = deviceDescriptionPtr->asynchCommandObjectID;

		FWSetFWCommandParams(fwCommandObjectID, fwIsochResourceManagerID, kFWCommandSyncFlag, nil, 0);
		
		FWGetAsynchCommandMaxRetries ( fwCommandObjectID, &saveMaxRT );
		FWSetAsynchCommandMaxRetries ( fwCommandObjectID, kMaxRetries );

		if (channel < 32)
			FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000224, (Ptr) &asynchCommandArgs[0], 4);
		else
			FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000228, (Ptr) &asynchCommandArgs[0], 4);

		// read channel allocated register
		status = FWRead(fwCommandObjectID);
		
		if (status == noErr)
		{
			// check if channel is reserved
			clippedChannel = (channel & 0x0000001f);
			if (!(asynchCommandArgs[0] & (0x80000000 >> clippedChannel)))
			{
				// channel is allocated, deallocate it
				asynchCommandArgs[1] = asynchCommandArgs[0] | (0x80000000 >> clippedChannel);
				
				// use existing settings except for length
				FWSetAsynchCommandLength(fwCommandObjectID, 8);
				
				status = FWCompareAndSwap(fwCommandObjectID);
			}	
		}
		FWSetAsynchCommandMaxRetries ( fwCommandObjectID, saveMaxRT );
	}

	return status;
}


// *** Must be task level only
OSStatus irmReleaseBandwidth(
	FWClientID					fwClientID,
	UInt32						fwBandwidthUnits,
	DeviceDescriptionPtr		deviceDescriptionPtr)
{
	FWReferenceID				fwIsochResourceManagerID;
	FWCommandObjectID			fwCommandObjectID = nil;
	UInt32						asynchCommandArgs[2], saveMaxRT;
	OSStatus					status = noErr;
	
	status = FWGetFWIsochResourceManagerID(fwClientID, &fwIsochResourceManagerID);
	
	if ((status == noErr) && (fwIsochResourceManagerID != (FWReferenceID) kInvalidFWIsochResourceManagerID))
	{
		// read IRM bandwidth register
		fwCommandObjectID = deviceDescriptionPtr->asynchCommandObjectID;

		FWSetFWCommandParams(fwCommandObjectID, fwIsochResourceManagerID, kFWCommandSyncFlag, nil, 0);
		
		FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000220, (Ptr) &asynchCommandArgs[0], 8);

		FWGetAsynchCommandMaxRetries ( fwCommandObjectID, &saveMaxRT );
		FWSetAsynchCommandMaxRetries ( fwCommandObjectID, kMaxRetries );

		// Set up arguments for add.
		// First parameter is the threshold, second is amount to add.
		// JKL *** Sony uses 0x1388 for starting available bandwidth which is nice since most of the
		// camcorders are allocating more bandwidth for one channel than necessary and not enough is
		// left for an additional channel
		// p1394 says IRM max bandwidth value should be 0x1333, 4915
		// asynchCommandArgs[0] = 0x1388;
		asynchCommandArgs[0] = 0x1333;
		asynchCommandArgs[1] = fwBandwidthUnits;

		// Add to bandwidth allocation.
		status = FWClippedAdd(fwCommandObjectID);

		FWSetAsynchCommandMaxRetries ( fwCommandObjectID, saveMaxRT );
	}

	return status;
}


// *** Must be task level only
OSStatus irmAllocateChannel(
	FWClientID					fwClientID,
	UInt32						*channel,
	DeviceDescriptionPtr		deviceDescriptionPtr)
{
	FWReferenceID				fwIsochResourceManagerID;
	FWCommandObjectID			fwCommandObjectID = nil;
	UInt32						asynchCommandArgs[2], channelArgs, saveMaxRT;
	OSStatus					status = noErr;
	
	status = FWGetFWIsochResourceManagerID(fwClientID, &fwIsochResourceManagerID);
	
	// JKL *** this is not right since noErr could get returned but not have an IRM
	if ((status == noErr) && (fwIsochResourceManagerID != (FWReferenceID) kInvalidFWIsochResourceManagerID))
	{
		// read IRM channel register
		fwCommandObjectID = deviceDescriptionPtr->asynchCommandObjectID;

		FWSetFWCommandParams(fwCommandObjectID, fwIsochResourceManagerID, kFWCommandSyncFlag, nil, 0);

		// populate both command args with the channel registers		
		FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000224, (Ptr) &asynchCommandArgs[0], 4);

		FWGetAsynchCommandMaxRetries ( fwCommandObjectID, &saveMaxRT );
		FWSetAsynchCommandMaxRetries ( fwCommandObjectID, kMaxRetries );
		
		// read channel hi allocated register
		status = FWRead(fwCommandObjectID);

		if (status == noErr)
		{
			FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000228, (Ptr) &asynchCommandArgs[1], 4);
	
			// read channel lo allocated register
			status = FWRead(fwCommandObjectID);
		}
		
		if (status == noErr)
		{
			// get next available channel
			if (*channel < 32)
				channelArgs = asynchCommandArgs[0];
			else
				channelArgs = asynchCommandArgs[1];
			while (! (channelArgs & (0x80000000 >> (*channel & 0x0000001f))))
			{
				*channel = *channel + 1;
				if (*channel > 31)
					channelArgs = asynchCommandArgs[1];
				
				if (*channel > 63)
					break;
			}
			
			// hopefully a channel got allocated
			if (*channel < 32)
			{
				// set channel hi address
				FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000224, (Ptr) &asynchCommandArgs[0], 8);
			}
			else if (*channel < 64)
			{
				// set channel lo address
				FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000228, (Ptr) &asynchCommandArgs[0], 8);
			}
			else
				status = noChannelsAvailableErr;
			
			if (status == noErr)
			{
				// clear allocated channel bit
				asynchCommandArgs[1] = asynchCommandArgs[0] & ~(0x80000000 >> (*channel & 0x01f));
				
				// do the command
				status = FWCompareAndSwap(fwCommandObjectID);
			}
		}

		FWSetAsynchCommandMaxRetries ( fwCommandObjectID, saveMaxRT );
	}

	return status;
}


// *** Must be task level only
OSStatus irmAllocateBandwidth(
	FWClientID					fwClientID,
	UInt32						fwBandwidthUnits,
	DeviceDescriptionPtr		deviceDescriptionPtr)
{
	FWReferenceID				fwIsochResourceManagerID;
	FWCommandObjectID			fwCommandObjectID = nil;
	UInt32						asynchCommandArgs[2], saveMaxRT;
	OSStatus					status = noErr;
	
	status = FWGetFWIsochResourceManagerID(fwClientID, &fwIsochResourceManagerID);
	
	// JKL *** this is not right since noErr could get returned but not have an IRM
	if ((status == noErr) && (fwIsochResourceManagerID != (FWReferenceID) kInvalidFWIsochResourceManagerID))
	{
		fwCommandObjectID = deviceDescriptionPtr->asynchCommandObjectID;

		FWSetFWCommandParams(fwCommandObjectID, fwIsochResourceManagerID, kFWCommandSyncFlag, nil, 0);

		// subtract bandwidth with a min at 0
		asynchCommandArgs[0] = 0;
		asynchCommandArgs[1] = fwBandwidthUnits;
		FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000220, (Ptr) &asynchCommandArgs[0], 8);

		FWGetAsynchCommandMaxRetries ( fwCommandObjectID, &saveMaxRT );
		FWSetAsynchCommandMaxRetries ( fwCommandObjectID, kMaxRetries );

		// Subtract from bandwidth allocation.
		status = FWThresholdSubtract(fwCommandObjectID);

		// Check if bandwidth was allocated.
		if (status == noErr)
		{
			// Bandwidth was allocated if old bandwidth value is greater
			// or equal to what we asked for.
			if (asynchCommandArgs[0] < asynchCommandArgs[1])
				status = insufficientBandwidthErr;
		}
		FWSetAsynchCommandMaxRetries ( fwCommandObjectID, saveMaxRT );
	}

	return status;
}


UInt32 irmCalculateBandwidthUnits(
	UInt32						pcrValue)
{						
	UInt32						pcrDataRate,
								pcrOverheadID,
								pcrPayload,
								fwBandwidthUnits;
	
	// set bandwidth units according to 61883 formula and PCR value
	// JKL *** all cameras do not set these values correctly, some set the
	// overhead too high and don't leave enough bandwidth for two streams
	
	// computer data rate multiplier
	pcrDataRate = (pcrValue & 0x0000c000) >> 14;
	switch (pcrDataRate)
	{
		case 0:
			pcrDataRate = 16;
			break;
		case 1:
			pcrDataRate = 8;
			break;
		case 2:
			pcrDataRate = 4;
			break;
	}
	
	pcrPayload = pcrValue & 0x03ff;
	pcrOverheadID = (pcrValue & 0x03c00) >> 10;
	
	// 0 is max for overhead
	if (!pcrOverheadID)
		pcrOverheadID = 16;
	
	fwBandwidthUnits = (pcrOverheadID * 32) + ((pcrPayload + 3) * pcrDataRate);
	
	return fwBandwidthUnits;
}


OSStatus cmpHandleBusReset(FWClientInterfaceParamsPtr pParams, UInt32 *pCommandAcceptance)
{
	DeviceDescriptionPtr pDeviceDescription;
	OSErr error = noErr;
	
	pDeviceDescription = (DeviceDescriptionPtr) pParams->fwClientSpecificData;
	
	RecordEventLogger( 'cmp ', 'busR', pDeviceDescription->resetStatus, error);
	if (pDeviceDescription->resetStatus == kResetInterrupted)
		pDeviceDescription->resetStatus = kNoResetsProcessing;

	cmpResetStageOne(pDeviceDescription);
	
	FWClientCommandIsComplete(pParams->fwClientCommandID, error);
	*pCommandAcceptance = kFWClientCommandAcceptNoMore;	
	
	return error;
}


//
// clear local PCR, FSL can do this for us
//
void cmpResetStageOne(DeviceDescriptionPtr pDeviceDescription)
{
	OSErr						error = noErr;
	
	RecordEventLogger( 'cmp ', 'res1', pDeviceDescription->resetStatus, error);
	if (pDeviceDescription->resetStatus == kNoResetsProcessing)
	{
		pDeviceDescription->resetStatus = kResetProcessing;

		if (pDeviceDescription->inputIsochChannelID)
		{
			error = cmpResetRead(0xf0000980 + (4 * pDeviceDescription->pcrIndex), pDeviceDescription->localFWReferenceID,
								 cmpResetStageTwo, pDeviceDescription);
		}
		else
		{
			error = cmpResetRead(0xf0000900 + (4 * pDeviceDescription->pcrIndex), pDeviceDescription->localFWReferenceID,
								 cmpResetStageTwo, pDeviceDescription);
		}
		
		if (noErr != error)
			pDeviceDescription->resetStatus = kNoResetsProcessing;
	}
	else
		pDeviceDescription->resetStatus = kResetInterrupted;
}


//
// complete clearing local PCR
//
void cmpResetStageTwo(FWCommandObjectID theObjectID, OSStatus status, UInt32 data)
{
	DeviceDescriptionPtr		pDeviceDescription = (DeviceDescriptionPtr) data;
	OSErr						error = noErr;
	
	RecordEventLogger( 'cmp ', 'res2', pDeviceDescription->resetStatus, status);
	if (pDeviceDescription->resetStatus == kResetProcessing)
	{
		if (status == noErr)
		{
			pDeviceDescription->buffer[0];
			if (pDeviceDescription->inputIsochChannelID)
			{
				pDeviceDescription->buffer[1] = pDeviceDescription->buffer[0] & 0x803f0000;
				error = cmpResetWrite(0xf0000980 + (4 * pDeviceDescription->pcrIndex), pDeviceDescription->localFWReferenceID,
									 cmpResetStageThree, pDeviceDescription);
			}
			else
			{
				pDeviceDescription->buffer[1] = pDeviceDescription->buffer[0] & 0x803fc3ff;
				error = cmpResetWrite(0xf0000900 + (4 * pDeviceDescription->pcrIndex), pDeviceDescription->localFWReferenceID,
									 cmpResetStageThree, pDeviceDescription);
			}
		}
		
		if ((noErr != status) || (noErr != error))
			pDeviceDescription->resetStatus = kNoResetsProcessing;
	}
	else
	{
		// another bus reset came in, start over
		pDeviceDescription->resetStatus = kNoResetsProcessing;
		cmpResetStageOne(pDeviceDescription);
	}
}


//
// re-allocate channel, read IRM channel
//
void cmpResetStageThree(FWCommandObjectID theObjectID, OSStatus status, UInt32 data)
{
	DeviceDescriptionPtr		pDeviceDescription = (DeviceDescriptionPtr) data;
	FWReferenceID				fwIsochResourceManagerID;
	OSErr						error = noErr;
	
	RecordEventLogger( 'cmp ', 'res3', pDeviceDescription->resetStatus, status);
	if (pDeviceDescription->resetStatus == kResetProcessing)
	{
		if (status == noErr)
		{
			error = FWGetFWIsochResourceManagerID(pDeviceDescription->fwClientID, &fwIsochResourceManagerID);
			
			if (error == noErr)
			{
				if (pDeviceDescription->cmpChannel < 32)
					error = cmpResetRead(0xf0000224, fwIsochResourceManagerID, cmpResetStageFour, pDeviceDescription);
				else
					error = cmpResetRead(0xf0000228, fwIsochResourceManagerID, cmpResetStageFour, pDeviceDescription);
			}
		}

		if ((noErr != status) || (noErr != error))
			pDeviceDescription->resetStatus = kNoResetsProcessing;
	}
	else
	{
		// another bus reset came in, start over
		pDeviceDescription->resetStatus = kNoResetsProcessing;
		cmpResetStageOne(pDeviceDescription);
	}
}


//
// complete re-allocating channel
// jkl, this might already be allocated
void cmpResetStageFour(FWCommandObjectID theObjectID, OSStatus status, UInt32 data)
{
	DeviceDescriptionPtr		pDeviceDescription = (DeviceDescriptionPtr) data;
	FWReferenceID				fwIsochResourceManagerID;
	OSErr						error = noErr;
	
	RecordEventLogger( 'cmp ', 'res4', pDeviceDescription->resetStatus, status);
	if (pDeviceDescription->resetStatus == kResetProcessing)
	{
		if (status == noErr)
		{
			error = FWGetFWIsochResourceManagerID(pDeviceDescription->fwClientID, &fwIsochResourceManagerID);
			
			if (error == noErr)
			{
				if (pDeviceDescription->cmpChannel < 32)
				{
					pDeviceDescription->buffer[1] = pDeviceDescription->buffer[0] & ~(0x80000000 >> pDeviceDescription->cmpChannel); 
					error = cmpResetWrite(0xf0000224, fwIsochResourceManagerID, cmpResetStageFive, pDeviceDescription);
				}
				else
				{
					pDeviceDescription->buffer[1] = pDeviceDescription->buffer[0] & ~(0x80000000 >> (pDeviceDescription->cmpChannel - 32)); 
					error = cmpResetWrite(0xf0000228, fwIsochResourceManagerID, cmpResetStageFive, pDeviceDescription);
				}
			}
		}

		if ((noErr != status) || (noErr != error))
			pDeviceDescription->resetStatus = kNoResetsProcessing;
	}
	else
	{
		// another bus reset came in, start over
		pDeviceDescription->resetStatus = kNoResetsProcessing;
		cmpResetStageOne(pDeviceDescription);
	}
}


//
// re-allocate bandwidth
// jkl, this might already be allocated
void cmpResetStageFive(FWCommandObjectID theObjectID, OSStatus status, UInt32 data)
{
	FWCommandObjectID			fwCommandObjectID;
	DeviceDescriptionPtr		pDeviceDescription = (DeviceDescriptionPtr) data;
	FWReferenceID				fwIsochResourceManagerID;
	OSErr						error = noErr;
	UInt32						saveMaxRT;
	
	RecordEventLogger( 'cmp ', 'res5', pDeviceDescription->resetStatus, status);
	if (pDeviceDescription->resetStatus == kResetProcessing)
	{
		if (status == noErr)
		{
			error = FWGetFWIsochResourceManagerID(pDeviceDescription->fwClientID, &fwIsochResourceManagerID);
		
			if (error == noErr)
			{
				fwCommandObjectID = pDeviceDescription->asynchCommandObjectID;
				
				// set command params for synchronous read
				error = FWSetFWCommandParams(fwCommandObjectID, fwIsochResourceManagerID, 0, cmpResetStageSix, (UInt32) pDeviceDescription);
				
				// set more command params for synchronous read
				if (error == noErr)
					error = FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, 0xf0000220, (Ptr) &(pDeviceDescription->buffer[0]), 8);
				
				FWGetAsynchCommandMaxRetries ( fwCommandObjectID, &saveMaxRT );
				FWSetAsynchCommandMaxRetries ( fwCommandObjectID, kMaxRetries );

				if (error == noErr)
				{
					pDeviceDescription->buffer[0] = 0;
					pDeviceDescription->buffer[1] = pDeviceDescription->cmpBandwidth;
					
					error = FWThresholdSubtract(fwCommandObjectID);
				}

				FWSetAsynchCommandMaxRetries ( fwCommandObjectID, saveMaxRT );
			}
		}

		if ((noErr != status) || (noErr != error))
			pDeviceDescription->resetStatus = kNoResetsProcessing;
	}
	else
	{
		// another bus reset came in, start over
		pDeviceDescription->resetStatus = kNoResetsProcessing;
		cmpResetStageOne(pDeviceDescription);
	}
}


//
// set oPCR, read it first
//
void cmpResetStageSix(FWCommandObjectID theObjectID, OSStatus status, UInt32 data)
{
	DeviceDescriptionPtr		pDeviceDescription = (DeviceDescriptionPtr) data;
	OSErr						error = noErr;
	
	RecordEventLogger( 'cmp ', 'res6', pDeviceDescription->resetStatus, status);
	if (pDeviceDescription->resetStatus == kResetProcessing)
	{
		if (status == noErr)
		{
			if (pDeviceDescription->inputIsochChannelID)
			{
				error = cmpResetRead(0xf0000904, pDeviceDescription->fwClientID,
									 cmpResetStageSeven, pDeviceDescription);
			}
			else
			{
				error = cmpResetRead(0xf0000900 + (4 * pDeviceDescription->pcrIndex), pDeviceDescription->localFWReferenceID,
									 cmpResetStageSeven, pDeviceDescription);
			}
		}

		if ((noErr != status) || (noErr != error))
			pDeviceDescription->resetStatus = kNoResetsProcessing;

	}
	else
	{
		// another bus reset came in, start over
		pDeviceDescription->resetStatus = kNoResetsProcessing;
		cmpResetStageOne(pDeviceDescription);
	}
}



//
// complete oPCR
//
void cmpResetStageSeven(FWCommandObjectID theObjectID, OSStatus status, UInt32 data)
{
	DeviceDescriptionPtr		pDeviceDescription = (DeviceDescriptionPtr) data;
	OSErr						error = noErr;
	
	RecordEventLogger( 'cmp ', 'res7', pDeviceDescription->resetStatus, status);
	if (pDeviceDescription->resetStatus == kResetProcessing)
	{
		if (status == noErr)
		{
			if (pDeviceDescription->inputIsochChannelID)
			{
				pDeviceDescription->buffer[1] = pDeviceDescription->buffer[0] + 0x01000000; 
				pDeviceDescription->buffer[1] |= (pDeviceDescription->cmpOverheadID << 10); 
				error = cmpResetWrite(0xf0000904, pDeviceDescription->fwClientID, cmpResetStageEight, pDeviceDescription);
			}
			else
			{
				pDeviceDescription->buffer[1] = pDeviceDescription->buffer[0] + 0x01000000; 
				pDeviceDescription->buffer[1] |= (pDeviceDescription->cmpOverheadID << 10); 
				error = cmpResetWrite(0xf0000900 + (4 * pDeviceDescription->pcrIndex), pDeviceDescription->localFWReferenceID,
									  cmpResetStageEight, pDeviceDescription);
			}
		}
	
		if ((noErr != status) || (noErr != error))
			pDeviceDescription->resetStatus = kNoResetsProcessing;
	}
	else
	{
		// another bus reset came in, start over
		pDeviceDescription->resetStatus = kNoResetsProcessing;
		cmpResetStageOne(pDeviceDescription);
	}
}


//
// set iPCR, read it first
//
void cmpResetStageEight(FWCommandObjectID theObjectID, OSStatus status, UInt32 data)
{
	DeviceDescriptionPtr		pDeviceDescription = (DeviceDescriptionPtr) data;
	OSErr						error = noErr;
	
	RecordEventLogger( 'cmp ', 'res8', pDeviceDescription->resetStatus, status);
	if (pDeviceDescription->resetStatus == kResetProcessing)
	{
		if (status == noErr)
		{
			if (pDeviceDescription->outputIsochChannelID)
			{
				error = cmpResetRead(0xf0000984, pDeviceDescription->fwClientID,
									 cmpResetStageNine, pDeviceDescription);
			}
			else
			{
				error = cmpResetRead(0xf0000980 + (4 * pDeviceDescription->pcrIndex), pDeviceDescription->localFWReferenceID,
									 cmpResetStageNine, pDeviceDescription);
			}
		}

		if ((noErr != status) || (noErr != error))
			pDeviceDescription->resetStatus = kNoResetsProcessing;
	}
	else
	{
		// another bus reset came in, start over
		pDeviceDescription->resetStatus = kNoResetsProcessing;
		cmpResetStageOne(pDeviceDescription);
	}
}


//
// complete iPCR
//
void cmpResetStageNine(FWCommandObjectID theObjectID, OSStatus status, UInt32 data)
{
	DeviceDescriptionPtr		pDeviceDescription = (DeviceDescriptionPtr) data;
	OSErr						error = noErr;
	
	RecordEventLogger( 'cmp ', 'res9', pDeviceDescription->resetStatus, status);
	if (pDeviceDescription->resetStatus == kResetProcessing)
	{
		if (status == noErr)
		{
			if (pDeviceDescription->outputIsochChannelID)
			{
				pDeviceDescription->buffer[1] = (pDeviceDescription->buffer[0] + 0x01000000) & 0xffc0ffff;
				pDeviceDescription->buffer[1] |= (pDeviceDescription->cmpChannel << 16);
				error = cmpResetWrite(0xf0000984, pDeviceDescription->fwClientID, cmpResetComplete, pDeviceDescription);
			}
			else
			{
				pDeviceDescription->buffer[1] = (pDeviceDescription->buffer[0] + 0x01000000) & 0xffc0ffff;
				pDeviceDescription->buffer[1] |= (pDeviceDescription->cmpChannel << 16);
				error = cmpResetWrite(0xf0000980 + (4 * pDeviceDescription->pcrIndex), pDeviceDescription->localFWReferenceID,
									  cmpResetComplete, pDeviceDescription);
			}
		}

		if ((noErr != status) || (noErr != error))
			pDeviceDescription->resetStatus = kNoResetsProcessing;

	}
	else
	{
		// another bus reset came in, start over
		pDeviceDescription->resetStatus = kNoResetsProcessing;
		cmpResetStageOne(pDeviceDescription);
	}
}


void cmpResetComplete(FWCommandObjectID theObjectID, OSStatus status, UInt32 data)
{
	DeviceDescriptionPtr		pDeviceDescription = (DeviceDescriptionPtr) data;
	OSErr						error = noErr;

	RecordEventLogger( 'cmp ', 'res ', 'comp', status);
	pDeviceDescription->resetStatus = kNoResetsProcessing;
}


//
// Do an asynch read
//
OSErr cmpResetRead (
	UInt32						offsetLo,
	FWClientID					clientID,
	FWCommandCompletionProcPtr	completionProc,
	DeviceDescriptionPtr		deviceDescriptionPtr)
{
	FWCommandObjectID			fwCommandObjectID;
	OSErr						error = noErr;
	UInt32						saveMaxRT;
	
	fwCommandObjectID = deviceDescriptionPtr->asynchCommandObjectID;
	
	// set command params for synchronous read
	error = FWSetFWCommandParams(fwCommandObjectID, clientID, 0, completionProc, (UInt32) deviceDescriptionPtr);
	
	// set more command params for synchronous read
	if (error == noErr)
		error = FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, offsetLo, (Ptr) &(deviceDescriptionPtr->buffer[0]), 4);
		
	FWGetAsynchCommandMaxRetries ( fwCommandObjectID, &saveMaxRT );
	FWSetAsynchCommandMaxRetries ( fwCommandObjectID, kMaxRetries );

	if (error == noErr)
		error = FWRead(fwCommandObjectID);
		
	FWSetAsynchCommandMaxRetries ( fwCommandObjectID, saveMaxRT );
		
	return error;
}
	

//
// Do an asynch write, really a lock
//
OSErr cmpResetWrite (
	UInt32						offsetLo,
	FWClientID					clientID,
	FWCommandCompletionProcPtr	completionProc,
	DeviceDescriptionPtr		deviceDescriptionPtr)
{
	FWCommandObjectID			fwCommandObjectID;
	OSErr						error = noErr;
	UInt32						saveMaxRT;
	
	fwCommandObjectID = deviceDescriptionPtr->asynchCommandObjectID;
	
	// set command params for synchronous read
	error = FWSetFWCommandParams(fwCommandObjectID, clientID, 0, completionProc, (UInt32) deviceDescriptionPtr);
	
	// set more command params for synchronous read
	if (error == noErr)
		error = FWSetCommonAsynchCommandParams(fwCommandObjectID, 0x0000ffff, offsetLo, (Ptr) &(deviceDescriptionPtr->buffer[0]), 8);
		
	FWGetAsynchCommandMaxRetries ( fwCommandObjectID, &saveMaxRT );
	FWSetAsynchCommandMaxRetries ( fwCommandObjectID, kMaxRetries );

	if (error == noErr)
		error = FWCompareAndSwap(fwCommandObjectID);
		
	FWSetAsynchCommandMaxRetries ( fwCommandObjectID, saveMaxRT );
		
	return error;
}
	

Boolean useCMP()
{
	Handle			h;
	Str63			fileName;
	CInfoPBRec		catInfoPB;
	FSSpec			fileSpec;
	SInt32			extfDirID;
	SInt16			extfIndex = 1;
	SInt16			extfVRefNum, fRef;
	SInt32			gestaltVers;
	OSErr			err;
	Boolean			useCMP = false;	// default is to not use it
	
	// use cmp only if FireWire 2.2 or greater
	err = Gestalt(gestaltFireWireVersion, &gestaltVers);
	if (err == noErr)
	{
		if (gestaltVers >= 0x0220)
			useCMP = true;
	}

	// if FireWire 2.2 or greater check if useGestalt resource is available and its value matches
	if (useCMP)
	{
		// get a shortcut to the extensions folder
		err = FindFolder(kOnSystemDisk, kExtensionFolderType, kDontCreateFolder, &extfVRefNum, &extfDirID);
		
		if (err == noErr)
		{
			// iterate through files in extensions folder
			while (err == noErr)
			{
				fileName[ 0 ]					= '\0';
				fileName[ 1 ]					= '\0';
				catInfoPB.hFileInfo.ioNamePtr	= &fileName[ 0 ];
				catInfoPB.hFileInfo.ioVRefNum	= extfVRefNum;
				catInfoPB.hFileInfo.ioFDirIndex	= extfIndex++;
				catInfoPB.hFileInfo.ioDirID		= extfDirID;
				err = PBGetCatInfoSync(&catInfoPB);
				if (err == noErr)
				{
					// find the file matching these attributes
					if ((catInfoPB.hFileInfo.ioFlAttrib & 0x10) != 0 ||
							(catInfoPB.hFileInfo.ioFlFndrInfo.fdType != 'INIT') ||
							(catInfoPB.hFileInfo.ioFlFndrInfo.fdCreator != 'dvfw'))
						continue;
					
					// found our file, make an FSSpec
					err = FSMakeFSSpec(extfVRefNum, extfDirID, catInfoPB.hFileInfo.ioNamePtr, &fileSpec);
									
					break;
					
				}
			}
		}
		
		if (err == noErr)
		{
			fRef = FSpOpenResFile(&fileSpec, fsRdPerm);
			err = ResError();
			if (err == noErr)
			{
				h = Get1Resource('ucmp', -20756);
				err = ResError();
				if ((err == noErr) && (h != nil))
					useCMP = true;
				else
					useCMP = false;
			
				CloseResFile(fRef);
			}
		}
		
		// turn off useCMP in case of any other error
		if (err != noErr)
			useCMP = false;
	}

	return useCMP;
}


OSErr enableRead(IsochComponentInstancePtr ih)
{
	FWCommandObjectID			isochChannelCommandObjectID = nil;
	DeviceDescriptionPtr		pDeviceDescription;
	SInt32						time;
	OSErr						error = noErr;
	
	error = findDeviceDescriptionforDevice(ih, ih->deviceID, &pDeviceDescription);
	FailMessageVal( error != noErr, error);
	
	if (error == noErr)
	{
	 	error = dclInitInput(pDeviceDescription, ih->gGlobals);		
		FailMessageVal( error != noErr, error);
	}
	
	if (error == noErr)
	{
		// JKL, how do we know the speed before doing CMP, IRM
		error = FWAllocateIsochronousChannelID(&pDeviceDescription->inputIsochChannelID, false, 30000000, kFWSpeed100MBit);
		FailMessageVal( error != noErr, error);
 		if (error != noErr)
 			disposeDCLInput(pDeviceDescription->pGlobalDVInData);
	}
	
	// add a talking client, camera
	if (error == noErr)
	{
		error = FWAddIsochronousChannelClient(pDeviceDescription->inputIsochChannelID, pDeviceDescription->fwClientID, (UInt32) pDeviceDescription, true);
		FailMessageVal( error != noErr, error);
		if (error != noErr)
		{
			FWDeallocateIsochronousChannelID(pDeviceDescription->inputIsochChannelID);
			pDeviceDescription->inputIsochChannelID = nil;
			disposeDCLInput(pDeviceDescription->pGlobalDVInData);
		}
	}
		
	// add a listening client, mac
	if (error == noErr)
	{
		error = FWAddIsochronousChannelClient(pDeviceDescription->inputIsochChannelID, pDeviceDescription->fwClientID, (UInt32) pDeviceDescription, false);
		FailMessageVal( error != noErr, error);
		if( error != noErr )
		{
			FWDeallocateIsochronousChannelID(pDeviceDescription->inputIsochChannelID);
			pDeviceDescription->inputIsochChannelID = nil;
 			disposeDCLInput(pDeviceDescription->pGlobalDVInData);
		}
	}
	
	// Initialize the isochronous channel
	if (error == noErr)
	{
		if (pDeviceDescription->componentGlobals->useCMP)
		{
			pDeviceDescription->resetStatus = kNoResetsProcessing;
			
			// allocate PCR space from FSL
			// JKL, for now should get pcr #1, what if we don't
			error = FWAllocatePCRAddressSpace(&pDeviceDescription->pcrID, pDeviceDescription->fwClientID,
											  (Ptr) &pDeviceDescription->pcrBuffer,
											  kFWAddressReadEnable | kFWAddressLockEnable |
											  kFWAddressLockRequestNotify | kFWAddressLockCompleteNotify,
											  (Ptr) pDeviceDescription, true /* inputPCR */, 1, &pDeviceDescription->pcrIndex);
			FailMessageVal( error != noErr, error);
			
	// JKL, not using lock call backs, could be used to respond to device trying to modify local PCR's
	//		if (error == noErr)
	//			error = FWSetFWClientLockCompleteProc(pDeviceDescription->fwClientID, pcrHandleLocalLock);
	//
	//		if (error == noErr)
	//			error = FWSetFWClientLockRequestProc(pDeviceDescription->fwClientID, pcrHandleLocalLock);

			// add bus reset proc
	 		if (error == noErr)
	 			error = FWSetFWClientBusManagementNotifyProc(pDeviceDescription->fwClientID, (FWClientResetNotifyProcPtr) cmpHandleBusReset);
	 		FailMessageVal( error != noErr, error);
		}

		// register for bus reset notification from FireWire for DV deviceChanged notification
		// unregister at release
	 	if (error == noErr)
		{
			error = FWSetFWClientResetNotifyProc(pDeviceDescription->fwClientID, handleBusReset);
	 		FailMessageVal( error != noErr, error);
		}
	
		if (error == noErr)
		{
			error = FWAllocateIsochChannelCommandObject(&isochChannelCommandObjectID);
			FailMessageVal( error != noErr, error);
		}
		
		if (error == noErr)
		{
			FWSetFWCommandParams(isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, kFWCommandSyncFlag, nil, 0);
			FWSetIsochChannelCommandIsochChannelID(isochChannelCommandObjectID, pDeviceDescription->inputIsochChannelID);
			error = FWInitializeIsochronousChannel(isochChannelCommandObjectID);
			FailMessageVal( error != noErr, error);
		}

		if	(error != noErr)
		{
			// release channel even though init failed, part of init (listener or talker) may have succeeded
			FWReleaseIsochronousChannel(isochChannelCommandObjectID);
			FWDeallocateIsochronousChannelID(pDeviceDescription->inputIsochChannelID);
			pDeviceDescription->inputIsochChannelID = nil;
			disposeDCLInput(pDeviceDescription->pGlobalDVInData);
		}
		else
		{
			// Start the isochronous channel
			error = FWSetFWCommandParams(isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, kFWCommandSyncFlag, nil, 0);
			FailMessageVal( error != noErr, error);
			error = FWSetIsochChannelCommandIsochChannelID(isochChannelCommandObjectID, pDeviceDescription->inputIsochChannelID);
			FailMessageVal( error != noErr, error);
			
			// JKL, revisit this.
			// delay because some (many) cameras send bad data immediately after you change the plug control registers
			// (at least that's my guess, It could be a bug somewhere in FSL... who knows...
			
			// we reprogram the PCRs with the above FWInitializeIsochronousChannel call
			
			// the delay was arrived at by good ole' trial and error			
			time = TickCount();
			while( TickCount() < (time + 7) )
			{
				// Yes...really do nothing
				;
			}	
			
			error = FWStartIsochronousChannel(isochChannelCommandObjectID);
			FailMessageVal( error != noErr, error);
			if (error != noErr)
			{
				FWReleaseIsochronousChannel(isochChannelCommandObjectID);
				FWDeallocateIsochronousChannelID(pDeviceDescription->inputIsochChannelID);
				pDeviceDescription->inputIsochChannelID = nil;
				disposeDCLInput(pDeviceDescription->pGlobalDVInData);
			}
		}
	}
	
	if (isochChannelCommandObjectID)
		FWDeallocateFWCommandObject(isochChannelCommandObjectID);
	
	// JKL, readEnabledNotification?
	if (error)
		FWSetFWClientResetNotifyProc(pDeviceDescription->fwClientID, nil);

	FailMessageVal( error != noErr, error);
	
	return error;
}


OSErr disableRead(IsochComponentInstancePtr ih)
{
	FWCommandObjectID			isochChannelCommandObjectID = nil;
	DeviceDescriptionPtr		pDeviceDescription;
	OSErr						error = noErr;
	
	error = findDeviceDescriptionforDevice(ih, ih->deviceID, &pDeviceDescription);

	if (error == noErr)
	{
		if (pDeviceDescription->inputIsochChannelID)
		{
			if (error == noErr)
				error = FWAllocateIsochChannelCommandObject(&isochChannelCommandObjectID);
		
			if (error == noErr)
			{
				// stop channel
				FWSetFWCommandParams(isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, kFWCommandSyncFlag, nil, 0);
				FWSetIsochChannelCommandIsochChannelID(isochChannelCommandObjectID, pDeviceDescription->inputIsochChannelID);
				FWStopIsochronousChannel(isochChannelCommandObjectID);
		
				// release channel
				FWSetFWCommandParams(isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, kFWCommandSyncFlag, nil, 0);
				FWSetIsochChannelCommandIsochChannelID(isochChannelCommandObjectID, pDeviceDescription->inputIsochChannelID);
				FWReleaseIsochronousChannel(isochChannelCommandObjectID);
				FWDeallocateIsochronousChannelID(pDeviceDescription->inputIsochChannelID);
				pDeviceDescription->inputIsochChannelID = 0;
		
				if (pDeviceDescription->componentGlobals->useCMP)
				{
					// JKL, should clear pcr before deallocating?
					FWDeallocateAddressSpace(pDeviceDescription->pcrID);
					pDeviceDescription->pcrID = 0;
					pDeviceDescription->pcrIndex = 0;
			
			 		FWSetFWClientBusManagementNotifyProc(pDeviceDescription->fwClientID, (FWClientResetNotifyProcPtr) nil);			

			// JKL, not using lock call backs or bus management yet
			//		FWSetFWClientLockCompleteProc(pDeviceDescription->fwClientID, (FWClientLockProcPtr) nil);
			//		FWSetFWClientLockRequestProc(pDeviceDescription->fwClientID, (FWClientLockProcPtr) nil);
				}
			}
			
			// unregister bus reset notification
			FWSetFWClientResetNotifyProc(pDeviceDescription->fwClientID, (FWClientResetNotifyProcPtr) nil);	

			disposeDCLInput(pDeviceDescription->pGlobalDVInData);
		}
	}
	return error;
}
	
	
OSErr enableWrite(IsochComponentInstancePtr ih)
{
	FWCommandObjectID			isochChannelCommandObjectID = nil;
	DeviceDescriptionPtr		pDeviceDescription;
	SInt32						time;
	OSErr						error = noErr;
	
	error = findDeviceDescriptionforDevice(ih, ih->deviceID, &pDeviceDescription);

	if (error == noErr)
 		error = dclInitOutput(pDeviceDescription, ih->gGlobals);		

	if (error == noErr)
	{
		// JKL, how do we know the speed before doing CMP, IRM
		error = FWAllocateIsochronousChannelID(&pDeviceDescription->outputIsochChannelID, false, 30000000, kFWSpeed100MBit);
 		if (error != noErr)
 			disposeDCLOutput(pDeviceDescription->pGlobalDVOutData);
	}

	// add a talking client, mac
	if (error == noErr)
	{
		error = FWAddIsochronousChannelClient(pDeviceDescription->outputIsochChannelID, pDeviceDescription->fwClientID, (UInt32) pDeviceDescription, true);
		if (error != noErr)
		{
			FWDeallocateIsochronousChannelID(pDeviceDescription->outputIsochChannelID);
			pDeviceDescription->outputIsochChannelID = nil;
			disposeDCLOutput(pDeviceDescription->pGlobalDVOutData);
		}
	}

	// add a listening client, camera
	if (error == noErr)
	{
		error = FWAddIsochronousChannelClient(pDeviceDescription->outputIsochChannelID, pDeviceDescription->fwClientID, (UInt32) pDeviceDescription, false);
		if (error != noErr)
		{
			FWDeallocateIsochronousChannelID(pDeviceDescription->outputIsochChannelID);
			pDeviceDescription->outputIsochChannelID = nil;
			disposeDCLOutput(pDeviceDescription->pGlobalDVOutData);
		}
	}
	
	// Initialize the isochronous channel
	if (error == noErr)
	{

		if (pDeviceDescription->componentGlobals->useCMP)
		{
			pDeviceDescription->resetStatus = kNoResetsProcessing;
	
			// allocate PCR space from FSL
			// JKL, for now should get pcr #1, what if we don't
			error = FWAllocatePCRAddressSpace(&pDeviceDescription->pcrID, pDeviceDescription->fwClientID,
											  (Ptr) &pDeviceDescription->pcrBuffer,
											  kFWAddressReadEnable | kFWAddressLockEnable |
											  kFWAddressLockRequestNotify | kFWAddressLockCompleteNotify,
											  (Ptr) pDeviceDescription, false /* outputPCR */, 1, &pDeviceDescription->pcrIndex);
			FailMessageVal( error != noErr, error);
			
	// JKL, not using lock call backs, could be used to respond to a device trying to modify local PCR's		
	//		if (error == noErr)
	//			error = FWSetFWClientLockCompleteProc(pDeviceDescription->fwClientID, cmpHandleLocalLock);
	//
	//		if (error == noErr)
	//			error = FWSetFWClientLockRequestProc(pDeviceDescription->fwClientID, cmpHandleLocalLock);

			// add bus reset proc
	 		if (error == noErr)
	 			error = FWSetFWClientBusManagementNotifyProc(pDeviceDescription->fwClientID, (FWClientResetNotifyProcPtr) cmpHandleBusReset);
	 		FailMessageVal( error != noErr, error);
		}

		// register for bus reset notification from FireWire for DV deviceChanged notification
		// unregister at release
	 	if (error == noErr)
		{
			error = FWSetFWClientResetNotifyProc(pDeviceDescription->fwClientID, handleBusReset);
	 		FailMessageVal( error != noErr, error);
		}
	
		if (error == noErr)
		{
			error = FWAllocateIsochChannelCommandObject(&isochChannelCommandObjectID);
			FailMessageVal( error != noErr, error);
		}
		
		if (error == noErr)
		{
			FWSetFWCommandParams(isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, kFWCommandSyncFlag, nil, 0);
			FWSetIsochChannelCommandIsochChannelID(isochChannelCommandObjectID, pDeviceDescription->outputIsochChannelID);		
			error = FWInitializeIsochronousChannel(isochChannelCommandObjectID);
		}

		if (error != noErr)
		{
			// release channel even though init failed, part of init (listener or talker) may have succeeded
			FWReleaseIsochronousChannel(isochChannelCommandObjectID);
			FWDeallocateIsochronousChannelID(pDeviceDescription->outputIsochChannelID);
			pDeviceDescription->outputIsochChannelID = nil;
			disposeDCLOutput(pDeviceDescription->pGlobalDVOutData);
		}
		else
		{
			// Start the isochronous channel
			error = FWSetFWCommandParams(isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, kFWCommandSyncFlag, nil, 0);
			error = FWSetIsochChannelCommandIsochChannelID(isochChannelCommandObjectID, pDeviceDescription->outputIsochChannelID);
		
			// delay because some (many) cameras send bad data immediately after you change the plug control registers
			// (at least that's my guess, It could be a bug somewhere in FSL... who knows...			
			// we reprogram the PCRs with the above FWInitializeIsochronousChannel call
			// the delay was arrived at by good ole' trial and error
		
			error = FWStartIsochronousChannel(isochChannelCommandObjectID);	
			if (error == noErr)
			{
				time = TickCount();
				while( TickCount() < (time + 60) )
				{
					// Yes...really do nothing
					;
				}	
			}
			else
			{
				FWReleaseIsochronousChannel(isochChannelCommandObjectID);
				FWDeallocateIsochronousChannelID(pDeviceDescription->outputIsochChannelID);
				pDeviceDescription->outputIsochChannelID = nil;
				disposeDCLOutput(pDeviceDescription->pGlobalDVOutData);
			}
		}
	}
	
	if (error)
		FWSetFWClientResetNotifyProc(pDeviceDescription->fwClientID, nil);

	if (isochChannelCommandObjectID)
		FWDeallocateFWCommandObject(isochChannelCommandObjectID);
	
	// JKL, writeEnabledNotification
	
	return error;
}


OSErr disableWrite(IsochComponentInstancePtr ih)
{
	FWCommandObjectID			isochChannelCommandObjectID = nil;
	DeviceDescriptionPtr		pDeviceDescription;
	OSErr						error = noErr;
	
	error = findDeviceDescriptionforDevice(ih, ih->deviceID, &pDeviceDescription);
	
	if (error == noErr)
	{
		if (pDeviceDescription->outputIsochChannelID)
		{
			if (error == noErr)
				error = FWAllocateIsochChannelCommandObject(&isochChannelCommandObjectID);
			
			if (error == noErr)
			{
				// JKL, need top delay stopping transmit until all queued frames are sent
				// originally had a isFinished call and then delayed 2 * ticks * howeverManyFramesAreQueued
				// stop channel
				FWSetFWCommandParams(isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, kFWCommandSyncFlag, nil, 0);
				FWSetIsochChannelCommandIsochChannelID(isochChannelCommandObjectID, pDeviceDescription->outputIsochChannelID);
				FWStopIsochronousChannel(isochChannelCommandObjectID);
		
				// release channel
				FWSetFWCommandParams(isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, kFWCommandSyncFlag, nil, 0);
				FWSetIsochChannelCommandIsochChannelID(isochChannelCommandObjectID, pDeviceDescription->outputIsochChannelID);
				FWReleaseIsochronousChannel(isochChannelCommandObjectID);
				FWDeallocateIsochronousChannelID(pDeviceDescription->outputIsochChannelID);
				pDeviceDescription->outputIsochChannelID = 0;
		
				if (pDeviceDescription->componentGlobals->useCMP)
				{
					// jkl, should clear pcr before deallocating
					FWDeallocateAddressSpace(pDeviceDescription->pcrID);
					pDeviceDescription->pcrID = 0;
					pDeviceDescription->pcrIndex = 0;
			
					FWSetFWClientBusManagementNotifyProc(pDeviceDescription->fwClientID, (FWClientResetNotifyProcPtr) nil);

			// JKL, not using lock call backs or bus management yet
			//		FWSetFWClientLockCompleteProc(pDeviceDescription->fwClientID, (FWClientLockProcPtr) nil);
			//		FWSetFWClientLockRequestProc(pDeviceDescription->fwClientID, (FWClientLockProcPtr) nil);
				}		
			}
			
			// unregister bus reset notification
			FWSetFWClientResetNotifyProc(pDeviceDescription->fwClientID, (FWClientResetNotifyProcPtr) nil);	

			disposeDCLOutput(pDeviceDescription->pGlobalDVOutData);
		}
	}
	return error;
}


OSErr dclInitOutput(DeviceDescriptionPtr pDeviceDescription,
		IsochComponentGlobals* isochComponentGlobals)
{	
	float						A, B, C, d, n;
	UInt32						numEmptyPacketsPerPlayBufferGroup;
	UInt32						transmitBuffersSize;
	DCLCommandPoolPtr			pDCLCommandPool;
	DCLCommandPtr				pDCLCommand;
	DCLCommandPtr				pFirstBufferGroupDCLCommand;
	DVGlobalOutPtr				pGlobalData;
	DCLLabelPtr					pUnderrunDCLLabel,
								pLoopDCLLabel,
								pBufferGroupDCLLabel,
								pDCLLabel;
	DCLTransferPacketPtr		pDCLTransferPacket;
	DCLCallProcPtr				pDCLCallProc;
	DCLSetTagSyncBitsPtr		pDCLSetTagSyncBits;
	DCLJumpPtr					pDCLJump,
								pBufferGroupDCLJump;
	DCLLabelPtr					pBufferGroupSkipEmptyPacketDCLLabel;
	DCLUpdateDCLListPtr			pDCLUpdateDCLList;
	DCLTimeStampPtr				pDCLTimeStamp;

	DVLocalOutPtr				pPlayBufferGroupData;
	UInt32						*pTransmitBuffer;
	UInt32						bufferGroupNum,
								dataPacketNum,
								numPackets;
	UInt32						emptyPacketNumerator;
	UInt32						playFrameRateNumerator, playFrameRateDenominator;
	UInt32						numDataPacketsPerPage;
	UInt32						pageSize;
	OSErr						error = noErr;
	
	// allocate space for global data
	pGlobalData = PoolAllocateResident(sizeof(DVGlobalOut), true);
	if (pGlobalData == nil)
		error = memFullErr;
	
	if (error == noErr)
	{
		pDeviceDescription->pGlobalDVOutData = pGlobalData;
		
		pGlobalData->isochComponentGlobals = isochComponentGlobals;
		pGlobalData->deviceID = pDeviceDescription->deviceID;
		pGlobalData->pBufferGroupDataList = nil;	
		pGlobalData->pDCLCommandPool = nil;
		pGlobalData->pDVFrameOutputData = nil;
		pGlobalData->pTransmitBuffers = nil;
		pGlobalData->isNTSC = (pDeviceDescription->standard == ntscIn);
		pGlobalData->localFWReferenceID = pDeviceDescription->localFWReferenceID;
		
		error = initDVFrameOutput(pDeviceDescription->standard, &pGlobalData->pDVFrameOutputData);
	}
	
	if (error == noErr)
	{
		error = FWCreateDCLProgram(&pGlobalData->DCLProgramID);
		pDeviceDescription->dclProgramID = pGlobalData->DCLProgramID;
	}
	
	if (error == noErr)
	{
		error = FWAllocateIsochChannelCommandObject(&pGlobalData->isochChannelCommandObjectID);
	}
	

	if (error == noErr)
	{
		if( pGlobalData->isNTSC )
		{
			pGlobalData->playFramePeriodNumerator = kNTSCPlayFramePeriodNumerator;
			pGlobalData->playFramePeriodDenominator = kNTSCPlayFramePeriodDenominator;
			playFrameRateNumerator = kNTSCFrameRateNumerator;
			playFrameRateDenominator = kNTSCFrameRateDenominator;
			pGlobalData->numDataPacketsPerFrame = kNTSCNumDataPacketsPerDVFrame;
		}
		else
		{
			pGlobalData->playFramePeriodNumerator = kPALPlayFramePeriodNumerator;
			pGlobalData->playFramePeriodDenominator = kPALPlayFramePeriodDenominator;
			playFrameRateNumerator = kPALFrameRateNumerator;
			playFrameRateDenominator = kPALFrameRateDenominator;
			pGlobalData->numDataPacketsPerFrame = kPALNumDataPacketsPerDVFrame;
		}
	
		// Compute nominal frame period cycle time.
		pGlobalData->nominalFrameCycleTime = convertFractionalSecondsToFWCycleTime
														(pGlobalData->playFramePeriodNumerator, pGlobalData->playFramePeriodDenominator);

		// Compute the number of data packets per empty packet.
		// If the frame rate is expressed as n/d, the number of data packets per buffer group
		// expressed as A, and the number of data packets per frame as C, then the number of
		// empty packets per buffer group B should be
		//
		// B = int (8000*d/n*A/C - A + 1)
		//
		// in order to ensure that the frame rate may be maintained by periodically reducing
		// the number of empty packets in a buffer group by 1.
		//
		A = (float) kNumDataPacketsPerPlayBufferGroup;
		C = (float) pGlobalData->numDataPacketsPerFrame;
		n = (float) playFrameRateNumerator;
		d = (float) playFrameRateDenominator;
		B = 8000.0*d/n*A/C - A + 1;
		numEmptyPacketsPerPlayBufferGroup = (UInt32) B;
	

		/////////////////////////////////////////
		// Allocate transmit buffers.
		//
		
		// Compute size.
	
		// to prevent packets crossing page boundaries
		// allocate buffer memory based on pages,
		// there is enough extra room in each page for the empty packets
		pageSize = GetLogicalPageSize();
		numDataPacketsPerPage = pageSize / (kDVPacketTransferSize + kDVPacketAlignSlop);
		transmitBuffersSize = kNumDataPacketsPerPlayBufferGroup / numDataPacketsPerPage * pageSize;
		transmitBuffersSize *= kNumPlayBufferGroups;
		
		// add an extra page to account for first page not being page aligned
		transmitBuffersSize += pageSize;
	
		// Allocate.
		pGlobalData->pTransmitBuffers = PoolAllocateResident(transmitBuffersSize, false);
		if (pGlobalData->pTransmitBuffers == nil)
			error = memFullErr;
		else
			pTransmitBuffer = (UInt32 *) pGlobalData->pTransmitBuffers;
	}
	
	if (error == noErr)
	{
	
		/////////////////////////////////////////
		// Start Up DCL Allocation Engine
		// Allocate DCL command pool.
		
		pDCLCommandPool = dclAllocateCommandPool();
		if (pDCLCommandPool == nil)
			error = memFullErr;
		else
			pGlobalData->pDCLCommandPool = pDCLCommandPool;
	}

	if (error == noErr)
	{
		////////////////////////////////////
		// Create DCL Program
		//
		
		// Initialize total packet count.
		pGlobalData->totalPackets = 0;
	
		// Create label for start of loop.
		pLoopDCLLabel = (DCLLabelPtr) dclAllocateCommand(pDCLCommandPool, sizeof (DCLLabel));
		//zzz check error
		pDCLCommand = (DCLCommandPtr) pLoopDCLLabel;
		
		pGlobalData->pDCLList = pDCLCommand;
		pLoopDCLLabel->opcode = kDCLLabelOp;
	
		// Set isoch packet tag bits to the way DV likes 'em
		pDCLSetTagSyncBits = (DCLSetTagSyncBitsPtr) dclAllocateCommand(pDCLCommandPool, sizeof (DCLSetTagSyncBits));
		//zzz check error
		pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLSetTagSyncBits;
		pDCLCommand = (DCLCommandPtr) pDCLSetTagSyncBits;
		pDCLSetTagSyncBits->opcode = kDCLSetTagSyncBitsOp;
		pDCLSetTagSyncBits->tagBits = 1;
		pDCLSetTagSyncBits->syncBits = 0;
	
		for (bufferGroupNum = 0; bufferGroupNum < kNumPlayBufferGroups; bufferGroupNum++)
		{
			// Allocate a buffer group data record.
			pPlayBufferGroupData = dclAllocatePlayBufferGroup(pGlobalData);
			if (pPlayBufferGroupData == nil)
			{
				error = memFullErr;
				goto bail;
			}
				
			
			// Initialize for loop.
			dataPacketNum = 0;
			numPackets = 0;
			emptyPacketNumerator = 0;
			pFirstBufferGroupDCLCommand = nil;
			pBufferGroupSkipEmptyPacketDCLLabel = nil;
	
			while (dataPacketNum < kNumDataPacketsPerPlayBufferGroup)
			{
				// Send a packet: CIP header + payload.
				pDCLTransferPacket = (DCLTransferPacketPtr) dclAllocateCommand(pDCLCommandPool, sizeof (DCLTransferPacket));
				if( pDCLTransferPacket == nil )
				{
					error = memFullErr;
					goto bail;
				}
				pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLTransferPacket;
				pDCLCommand = (DCLCommandPtr) pDCLTransferPacket;
				pDCLTransferPacket->opcode = kDCLSendPacketStartOp;
				pDCLTransferPacket->size = kDVPacketTransferSize;
	
				// check for buffer crossing page
				if (((UInt32) (pTransmitBuffer + kDVPacketAlignOffset) & 0x0fff) < (kDVPacketAlignOffset * 4))
				{
					// if it does, increment buffer pointer
					// and lop off page rollover to start at next page
					pTransmitBuffer += kDVPacketAlignOffset;
					pTransmitBuffer = (UInt32 *) ((UInt32) pTransmitBuffer & 0xfffff000);
				}
	
				pDCLTransferPacket->buffer = (Ptr) pTransmitBuffer;
				// increment by 496 bytes to maintain cache alignment
				pTransmitBuffer += kDVPacketAlignOffset;
	
				// Save first data packet DCL command.
				if (pFirstBufferGroupDCLCommand == nil)
					pFirstBufferGroupDCLCommand = (DCLCommandPtr) pDCLCommand;
	
				dataPacketNum++;
				numPackets++;
				emptyPacketNumerator += numEmptyPacketsPerPlayBufferGroup;
	
				if (emptyPacketNumerator >= kNumDataPacketsPerPlayBufferGroup)
				{
					// Add skip jump if this is the first empty packet in the buffer group.
					if (pBufferGroupSkipEmptyPacketDCLLabel == nil)
					{
						pDCLJump = (DCLJumpPtr) dclAllocateCommand(pDCLCommandPool, sizeof (DCLJump));
						if( pDCLJump == nil )
						{	
							error = memFullErr;
							goto bail;
						}
						pPlayBufferGroupData->pBufferGroupSkipEmptyPacketDCLJump = pDCLJump;
						pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLJump;
						pDCLCommand = (DCLCommandPtr) pDCLJump;
						pDCLJump->opcode = kDCLJumpOp | kFWDCLOpDynamicFlag;
	
						pDCLLabel = (DCLLabelPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLLabel));
						if( pDCLLabel == nil )
						{	
							error = memFullErr;
							goto bail;
						}
						pPlayBufferGroupData->pBufferGroupDontSkipEmptyPacketDCLLabel = pDCLLabel;
						pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLLabel;
						pDCLCommand = (DCLCommandPtr) pDCLLabel;
						pDCLLabel->opcode = kDCLLabelOp;
	
						pDCLJump->pJumpDCLLabel = pDCLLabel;
					}
	
					// Send a packet.
					// Just CIP header.
					pDCLTransferPacket = (DCLTransferPacketPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLTransferPacket));
					if( pDCLTransferPacket == nil )
					{
						error = memFullErr;
						goto bail;
					}
					pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLTransferPacket;
					pDCLCommand = (DCLCommandPtr) pDCLTransferPacket;
					pDCLTransferPacket->opcode = kDCLSendPacketStartOp;
					pDCLTransferPacket->buffer = (Ptr) pTransmitBuffer;
					pDCLTransferPacket->size = kDVPacketCIPSize;
	
					// increment 16 bytes to maintain alignment
					pTransmitBuffer += kDVEmptyPacketAlignOffset;
					numPackets++;
					emptyPacketNumerator -= kNumDataPacketsPerPlayBufferGroup;
	
					// Add skip jump label if this is the first empty packet in the
					// buffer group.
					if (pBufferGroupSkipEmptyPacketDCLLabel == nil)
					{
						// Add skip label.
						pDCLLabel = (DCLLabelPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLLabel));
						if( pDCLLabel == nil )
						{
							error = memFullErr;
							goto bail;
						}
						pBufferGroupSkipEmptyPacketDCLLabel = pDCLLabel;
						pPlayBufferGroupData->pBufferGroupSkipEmptyPacketDCLLabel = pBufferGroupSkipEmptyPacketDCLLabel;
						pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLLabel;
						pDCLCommand = (DCLCommandPtr) pDCLLabel;
						pDCLLabel->opcode = kDCLLabelOp;
					}
				}
			}
	
			// Save number of packets in this buffer group, DCL update list size, and last
			// DCL command.
			pPlayBufferGroupData->numPackets = numPackets;
			pPlayBufferGroupData->pFirstBufferGroupDCLCommand = pFirstBufferGroupDCLCommand;
			pPlayBufferGroupData->pLastBufferGroupDCLCommand = (DCLCommandPtr) pDCLCommand;
	
			// Create buffer group update list.
			createDVPlayBufferGroupUpdateList( pPlayBufferGroupData );
	
			// Update total packet count.
			pGlobalData->totalPackets += numPackets;
	
			// Create end of buffer group jump.
			pBufferGroupDCLJump = (DCLJumpPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLJump));
			if( pBufferGroupDCLJump == nil )
			{
				error = memFullErr;
				goto bail;
			}
			pPlayBufferGroupData->pEndOfBufferGroupDCLJump = pBufferGroupDCLJump;
			pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pBufferGroupDCLJump;
			pDCLCommand = (DCLCommandPtr) pBufferGroupDCLJump;
			pBufferGroupDCLJump->opcode = kDCLJumpOp | kFWDCLOpDynamicFlag;
	
			// Create label for end of buffer group.
			pBufferGroupDCLLabel = (DCLLabelPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLLabel));
			if( pBufferGroupDCLLabel == nil )
			{
				error = memFullErr;
				goto bail;
			}
			pPlayBufferGroupData->pEndOfBufferGroupDCLLabel = pBufferGroupDCLLabel;
			pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pBufferGroupDCLLabel;
			pDCLCommand = (DCLCommandPtr) pBufferGroupDCLLabel;
			pBufferGroupDCLLabel->opcode = kDCLLabelOp;
	
			// Set end of buffer group jump to jump to end of buffer group.
			pBufferGroupDCLJump->pJumpDCLLabel = pBufferGroupDCLLabel;
	
			// Get time stamp at end of buffer group.
			pDCLTimeStamp = (DCLTimeStampPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLTimeStamp));
			if( pDCLTimeStamp == nil )
			{
				error = memFullErr;
				goto bail;
			}
			pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLTimeStamp;
			pDCLCommand = (DCLCommandPtr) pDCLTimeStamp;
			pDCLTimeStamp->opcode = kDCLTimeStampOp;
			pPlayBufferGroupData->pBufferGroupDCLTimeStamp = pDCLTimeStamp;
			pPlayBufferGroupData->timeStampUpdateDCLList = (DCLCommandPtr) pDCLTimeStamp;
	
			// Create update DCL list to update time stamp.
			pDCLUpdateDCLList = (DCLUpdateDCLListPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLUpdateDCLList));
			if( pDCLUpdateDCLList == nil )
			{
				error = memFullErr;
				goto bail;
			}
			pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLUpdateDCLList;
			pDCLCommand = (DCLCommandPtr) pDCLUpdateDCLList;
			pDCLUpdateDCLList->opcode = kDCLUpdateDCLListOp;
			pDCLUpdateDCLList->dclCommandList = &(pPlayBufferGroupData->timeStampUpdateDCLList);
			pDCLUpdateDCLList->numDCLCommands = 1;
	
			// Call a proc at end of buffer group.
			pDCLCallProc = (DCLCallProcPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLCallProc));
			if( pDCLCallProc == nil )
			{
				error = memFullErr;
				goto bail;
			}
			pPlayBufferGroupData->pBufferGroupDCLCallProc = pDCLCallProc;
			pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLCallProc;
			pDCLCommand = (DCLCommandPtr) pDCLCallProc;
			pDCLCallProc->opcode = kDCLCallProcOp;
			pDCLCallProc->proc = handleDVOutput;
			pDCLCallProc->procData = (UInt32) pPlayBufferGroupData;
	
			// Create update DCL list to update buffers.
			pDCLUpdateDCLList = (DCLUpdateDCLListPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLUpdateDCLList));
			if( pDCLUpdateDCLList == nil )
			{
				error = memFullErr;
				goto bail;
			}
			pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLUpdateDCLList;
			pDCLCommand = (DCLCommandPtr) pDCLUpdateDCLList;
			pDCLUpdateDCLList->opcode = kDCLUpdateDCLListOp;
			pDCLUpdateDCLList->dclCommandList = pPlayBufferGroupData->bufferGroupUpdateDCLList;
			pDCLUpdateDCLList->numDCLCommands = pPlayBufferGroupData->updateListSize;
		}
	
		// Loop to first buffer group.
		pDCLJump = (DCLJumpPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLJump));
		if( pDCLJump == nil )
		{
			error = memFullErr;
			goto bail;
		}
		pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLJump;
		pDCLCommand = (DCLCommandPtr) pDCLJump;
		pDCLJump->opcode = kDCLJumpOp;
		pDCLJump->pJumpDCLLabel = pLoopDCLLabel;
		
		// Create label for underrun.
		pUnderrunDCLLabel = (DCLLabelPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLLabel));
		if( pUnderrunDCLLabel == nil )
		{
			error = memFullErr;
			goto bail;
		}
		pGlobalData->pUnderrunDCLLabel = pUnderrunDCLLabel;
		pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pUnderrunDCLLabel;
		pDCLCommand = (DCLCommandPtr) pUnderrunDCLLabel;
		pUnderrunDCLLabel->opcode = kDCLLabelOp;
	
		// Set last buffer group's jump DCL to jump to underrun.
		pBufferGroupDCLJump->pJumpDCLLabel = pUnderrunDCLLabel;
	
		// Call underrun proc.
		// This is the last command.
		pDCLCallProc = (DCLCallProcPtr) dclAllocateCommand (pDCLCommandPool, sizeof (DCLCallProc));
		if( pDCLCallProc == nil )
		{
			error = memFullErr;
			goto bail;
		}	
		pDCLCommand->pNextDCLCommand = (DCLCommandPtr) pDCLCallProc;
		pDCLCallProc->pNextDCLCommand = nil;
		pDCLCallProc->opcode = kDCLCallProcOp;
		pDCLCallProc->proc = handleDVOutputUnderrun;
		pDCLCallProc->procData = (UInt32) pDeviceDescription;
	
		// Initialize number of active play packets.
		pGlobalData->activePackets = pGlobalData->totalPackets;
	}

	// Set up all of the buffer groups.
	if (error == noErr)
	{
		pGlobalData->nextSYT = kPlaySYTDelay;
		pGlobalData->nextDBC = 0;
		pGlobalData->nextDataPacketNum = 0;
 		pGlobalData->pImageBuffer = nil;
		pPlayBufferGroupData = pGlobalData->pBufferGroupDataList;
		for (bufferGroupNum = 0; bufferGroupNum < kNumPlayBufferGroups; bufferGroupNum++)
		{
			updateDVOutputBuffers( (DCLCommandPtr) pPlayBufferGroupData->pBufferGroupDCLCallProc );
			pPlayBufferGroupData = pPlayBufferGroupData->pNextLocalData;
		}
	}

	if (error == noErr)
		error = FWSetDCLProgramStart(pGlobalData->DCLProgramID, pGlobalData->pDCLList);

// jkl, do we need to do this?		
// 	if (error == noErr)
// 		error = FWSetDCLProgramStartEvent(pGlobalData->DCLProgramID, kFWDCLCycleEvent, 0, BitRange (12, 15));
			
bail:
	// if we had a problem, lets clean it up
	if	(error != noErr)
	{
		disposeDCLOutput(pDeviceDescription->pGlobalDVOutData);
		pGlobalData = nil;
	}
	
	return error;
}


////////////////////////////////////////////////////////////////////////////////
//
// DVHandleOutput
//
//   This routine handles isochronous sending of DV data.
//

void handleDVOutput(DCLCommandPtr pDCLCommandPtr)
{
	DCLCallProcPtr				pDCLCallProc;
	DCLTimeStampPtr				pDCLTimeStamp;
	DVLocalOutPtr				pLocalData;
	DVGlobalOutPtr				pGlobalData;
	DVLocalOutPtr				pPrevLocalData;
	UInt32						nominalFrameCycleTime;
	UInt32						fractionalFrameCycleCount,
								fractionalFrameCycleOffset;
	SInt32						timeDrift;
	UInt32						cycleDrift;
	UInt32						projectedTimeStamp,
								projectedSYT;

	// Recast pDCLCommandPtr.
	pDCLCallProc = (DCLCallProcPtr) pDCLCommandPtr;

	// Get data for buffer group and driver data.
	pLocalData = (DVLocalOutPtr) pDCLCallProc->procData;
	pPrevLocalData = pLocalData->pPrevLocalData;
	pGlobalData = pLocalData->pGlobalData;
	nominalFrameCycleTime = pGlobalData->nominalFrameCycleTime;

	// Undo skipping empty packet if we're currently skipping a packet.
	if (pLocalData->skippingEmptyPacket)
	{
		FWModifyDCLJump( pGlobalData->DCLProgramID, pLocalData->pBufferGroupSkipEmptyPacketDCLJump, pLocalData->pBufferGroupDontSkipEmptyPacketDCLLabel);
		pGlobalData->activePackets++;
		pLocalData->skippingEmptyPacket = false;
	}

	// Compute time drift.

	// Compute the projected time stamp value for the first packet of the current
	// buffer group the next time this proc is called for the current buffer group.

	// Start at time stamp of first packet in next buffer group to be sent.
	pDCLTimeStamp = pLocalData->pBufferGroupDCLTimeStamp;
	projectedTimeStamp = pDCLTimeStamp->timeStamp;
 	projectedTimeStamp = addFWCycleTimeToFWCycleTime(projectedTimeStamp, 1 << 12);

	// Add the total number of cycles for all active buffer group packets.
	projectedTimeStamp = addFWCycleTimeToFWCycleTime(projectedTimeStamp, pGlobalData->activePackets << 12);

	// Subtract the number of cycles for all packets in the current buffer group.
	projectedTimeStamp = subtractFWCycleTimeFromFWCycleTime(projectedTimeStamp, pLocalData->numPackets << 12);

	// Compute the projected SYT value for the first packet of the current buffer group
	// the next time this proc is called for the current buffer group.

	// Start with the SYT value to use for the first packet of the next frame.
	projectedSYT = pGlobalData->nextSYT;

	// Subtract the SYT offset between frames.
	projectedSYT = subtractFWCycleTimeFromFWCycleTime(projectedSYT, nominalFrameCycleTime);

	// Add the fraction of the SYT offset between the start of the frame and the
	// first data packet for the current buffer group.
	fractionalFrameCycleOffset =
		((nominalFrameCycleTime & 0x0FFF) * pGlobalData->nextDataPacketNum) /
		pGlobalData->numDataPacketsPerFrame;

	fractionalFrameCycleCount =
		((nominalFrameCycleTime & 0x01FFF000) * pGlobalData->nextDataPacketNum) /
		pGlobalData->numDataPacketsPerFrame;
	fractionalFrameCycleCount =
		(fractionalFrameCycleCount & 0x01FFF000) +
		(((fractionalFrameCycleCount & 0x0FFF) * 3072) / 4096);

	projectedSYT = addFWCycleTimeToFWCycleTime (projectedSYT, fractionalFrameCycleOffset);
	projectedSYT = addFWCycleTimeToFWCycleTime (projectedSYT, fractionalFrameCycleCount);

	// The time drift is the difference between the projected time stamp and SYT.
	// We must convert the time drift to cycles.
	cycleDrift = addFWCycleTimeToFWCycleTime(projectedTimeStamp, kPlaySYTDelay << 12);
	cycleDrift = subtractFWCycleTimeFromFWCycleTime(cycleDrift, projectedSYT);
	timeDrift = (cycleDrift >> 12) & 0x0F;

	// Skip an empty packet if we're drifting.
	// Only consider positive drifting.
	if ((timeDrift > 0) && (timeDrift < 8))
	{
		FWModifyDCLJump (pGlobalData->DCLProgramID, pLocalData->pBufferGroupSkipEmptyPacketDCLJump, pLocalData->pBufferGroupSkipEmptyPacketDCLLabel);
		pGlobalData->activePackets--;
		pLocalData->skippingEmptyPacket = true;
	}

	updateDVOutputBuffers( pDCLCommandPtr );

	// Update DCL jumps to call underrun proc after this buffer group.
	//zzz check errors.
	FWModifyDCLJump (pGlobalData->DCLProgramID,
					 pLocalData->pEndOfBufferGroupDCLJump,
					 pGlobalData->pUnderrunDCLLabel);
	FWModifyDCLJump (pGlobalData->DCLProgramID,
					 pPrevLocalData->pEndOfBufferGroupDCLJump,
					 pPrevLocalData->pEndOfBufferGroupDCLLabel);
}

////////////////////////////////////////////////////////////////////////////////
//
// DVUpdateOutputBuffers
//
//   This routine updates the buffers for sending DV data.
//

void updateDVOutputBuffers(DCLCommandPtr pDCLCommandPtr)
{
	DCLCommandPtr				pCurrentDCLCommand;
	DCLCallProcPtr				pDCLCallProc;
	DCLTransferPacketPtr		pDCLTransferPacket;
	DVLocalOutPtr				pLocalData,
								pPrevLocalData;
	DVGlobalOutPtr				pGlobalData;
	UInt32						localNodeID;
	UInt32						generation;
	UInt32						nominalFrameCycleTime;
	UInt32						syt;
	UInt32						*pBuffer,
								*pImageBuffer,
								*pLastImageBuffer;
	UInt32						packetNum,
								dataPacketNum,
								numPackets;
	UInt32						dbc;
	
	// Recast pDCLCommandPtr.
	pDCLCallProc = (DCLCallProcPtr) pDCLCommandPtr;

	// Get data for buffer group.
	pLocalData = (DVLocalOutPtr) pDCLCallProc->procData;

	// Get driver data and first DCL command.
	pGlobalData = pLocalData->pGlobalData;
	pCurrentDCLCommand = pLocalData->pFirstBufferGroupDCLCommand;
	nominalFrameCycleTime = pGlobalData->nominalFrameCycleTime;

	// Get data for previous buffer group.
	pPrevLocalData = pLocalData->pPrevLocalData;
	syt = pGlobalData->nextSYT;
	dbc = pGlobalData->nextDBC;
	dataPacketNum = pGlobalData->nextDataPacketNum;

	// Get local node ID.
	FWGetNodeID( pGlobalData->localFWReferenceID, &localNodeID, &generation);

	// Get first send packet command for this buffer group.
	while (pCurrentDCLCommand->opcode != kDCLSendPacketStartOp)
		pCurrentDCLCommand = pCurrentDCLCommand->pNextDCLCommand;
	pDCLTransferPacket = (DCLTransferPacketPtr) pCurrentDCLCommand;

	// Update the packet buffers.
	numPackets = pLocalData->numPackets;
	
	// Get the next frame to output
 	if( pGlobalData->pImageBuffer == nil )
 		  getNextFullOutputFrame( pGlobalData->pDVFrameOutputData, &(pGlobalData->pImageBuffer) );
	
 	pImageBuffer = ( pGlobalData->pImageBuffer + (kDVPacketQuadSize * dataPacketNum) );
	for( packetNum = 0; packetNum < numPackets; packetNum++)
	{
		// Set up packet header.
		pBuffer = (UInt32 *) pDCLTransferPacket->buffer;
		pBuffer[0] = 0x00780000 | (localNodeID << 24) | (dbc & 0xFF);
		if( pGlobalData->isNTSC )
			pBuffer[1] = 0x8000FFFF;
		else
			pBuffer[1] = 0x8080FFFF; // set PAL bit

		// if not an empty packet
		if (pDCLTransferPacket->size == 488)
		{
			// Set SYT field if this is the first data packet in the frame.
			if (dataPacketNum == 0)
			{
				if( pGlobalData->isNTSC )
					pBuffer[1] = 0x80000000 | (syt & 0xFFFF);
				else
					pBuffer[1] = 0x80800000 | (syt & 0xFFFF); // PAL bit
					
				syt = addFWCycleTimeToFWCycleTime(syt, pGlobalData->nominalFrameCycleTime);		
			}
		
			// Copy data into packet.
			BlockMoveData(pImageBuffer, pDCLTransferPacket->buffer + kDVPacketCIPSize, kDVPacketDataSize);
			pImageBuffer += 120;
			dbc++;
			dataPacketNum++;
		
			// check if frame is done
			if (dataPacketNum == pGlobalData->numDataPacketsPerFrame )
			{
 				IDHDVCompleteEvent	event;
 				
				event.eventHeader.deviceID 	= pGlobalData->deviceID;
				event.eventHeader.event 	= kIDHPrivateEventWriteComplete;
				event.frameBuffer			= (Ptr) pImageBuffer;
 				event.fwCycleTime			= syt;
				
				postEvent(
						pGlobalData->isochComponentGlobals,
						pGlobalData->deviceID,
						kIDHPrivateEventWriteComplete,
						(void*)&event);

				dataPacketNum = 0;
				pLastImageBuffer = pGlobalData->pImageBuffer;
				getNextFullOutputFrame( pGlobalData->pDVFrameOutputData, &(pGlobalData->pImageBuffer) );
				pImageBuffer = pGlobalData->pImageBuffer;
	
				// Mute the audio on repeating frames, based on repeating frame sequences
				if (pImageBuffer == pLastImageBuffer)
				{
					UInt32	i,j,k,n;
					UInt8	*tPtr;
					
					
					{
						// Get DSF flag in byte 3 of header (Blue Book p. 113)
						tPtr = (UInt8 *)pImageBuffer;
						if ((tPtr[3] &= 0x80) == 0)
							n=10;							// ntsc			
						else
							n=12;							// pal
							
						// Go thru the frame (10 times for ntsc, 12 for pal)	
						for (i=0;i<n;i++) 		
						{

							// Yet another attempt ...
							// Mute all the audio samples
							
							for (i=0;i<n;i++)
							{
								for (j=0;j<9;j++)
								{
									tPtr = (UInt8 *)pImageBuffer + (i * 12000) + ((j * 16 + 6) * 80) + 8;
									for (k=0;k<72;k++)
										*tPtr++ = 0x0;
								}
								
							}
						}
					}
				}
			}
		}
		
		
		// Find next send packet start command.
		pCurrentDCLCommand = pCurrentDCLCommand->pNextDCLCommand;
		while (pCurrentDCLCommand != nil)
		{
			if (pCurrentDCLCommand->opcode != kDCLSendPacketStartOp)
				pCurrentDCLCommand = pCurrentDCLCommand->pNextDCLCommand;
			else
				break;
		}
		pDCLTransferPacket = (DCLTransferPacketPtr) pCurrentDCLCommand;
	}
	pGlobalData->nextSYT = syt;
	pGlobalData->nextDBC = dbc;
	pGlobalData->nextDataPacketNum = dataPacketNum;

	// Call to update DCL's if we need to.  We have to do this on underrun because the update
	// DCL command won't be run to reflect the changes we've made here.
	if (pLocalData->needsUpdate)
	{
		FWUpdateDCLList (pGlobalData->DCLProgramID, pLocalData->bufferGroupUpdateDCLList, pLocalData->updateListSize);
		pLocalData->needsUpdate = false;
	}
}

////////////////////////////////////////////////////////////////////////////////
//
// DVHandleOutputUnderrun
//
//   This routine handles underruns for sending DV data.
//

void handleDVOutputUnderrun(DCLCommandPtr pDCLCommandPtr)
{
	DCLCallProcPtr				pDCLCallProc;
	DeviceDescriptionPtr		pDeviceDescription;
	FWCommandObjectID			isochChannelCommandObjectID;

	// Recast pDCLCommandPtr.
	pDCLCallProc = (DCLCallProcPtr) pDCLCommandPtr;

	// Get driver data.
	pDeviceDescription = (DeviceDescriptionPtr) pDCLCallProc->procData;

	// Set up params for stopping isochronous channel.
	isochChannelCommandObjectID = pDeviceDescription->pGlobalDVOutData->isochChannelCommandObjectID;
	FWSetFWCommandParams(isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, 0, handleDVOutputUnderrunCompletion, (UInt32) pDCLCallProc);
	FWSetIsochChannelCommandIsochChannelID(isochChannelCommandObjectID, pDeviceDescription->outputIsochChannelID);

	// Stop the isochronous channel.
	FWStopIsochronousChannel(isochChannelCommandObjectID);
}


////////////////////////////////////////////////////////////////////////////////
//
// DVHandleOuputUnderrunCompletion
//
//   This routine handles underruns for sending DV data.
//

void handleDVOutputUnderrunCompletion (
	FWCommandObjectID			fwCommandObjectID,
	OSStatus					commandStatus,
	UInt32						completionProcData)
{
	DCLCallProcPtr				pDCLCallProc;
	DeviceDescriptionPtr		pDeviceDescription;
	DVLocalOutPtr				pDVPlayBufferGroupData;
	FWCommandObjectID			isochChannelCommandObjectID;
	UInt32						bufferGroupNum;

	// Get DCL call proc data.
	pDCLCallProc = (DCLCallProcPtr) completionProcData;

	// Get driver data.
	pDeviceDescription = (DeviceDescriptionPtr) pDCLCallProc->procData;

	// Reset next SYT, dbc, and data packet num.
	pDeviceDescription->pGlobalDVOutData->nextSYT = kPlaySYTDelay;
	pDeviceDescription->pGlobalDVOutData->nextDBC = 0;
	pDeviceDescription->pGlobalDVOutData->nextDataPacketNum = 0;
	pDeviceDescription->pGlobalDVOutData->pImageBuffer = nil;

	// Reset up all of the buffer groups.
	pDVPlayBufferGroupData = pDeviceDescription->pGlobalDVOutData->pBufferGroupDataList;
	for (bufferGroupNum = 0; bufferGroupNum < kNumPlayBufferGroups; bufferGroupNum++)
	{
		// Update buffer group buffers.
		// jkl, no update needed, Lynx fwim and OHCI fwim don't do anything for this anyway
//		pDVPlayBufferGroupData->needsUpdate = true;
		updateDVOutputBuffers((DCLCommandPtr) pDVPlayBufferGroupData->pBufferGroupDCLCallProc);

		// Update buffer group jump DCL.
		if (bufferGroupNum < (kNumPlayBufferGroups - 1))
		{
			FWModifyDCLJump (pDeviceDescription->pGlobalDVOutData->DCLProgramID,
							 pDVPlayBufferGroupData->pEndOfBufferGroupDCLJump,
							 pDVPlayBufferGroupData->pEndOfBufferGroupDCLLabel);
		}
		else
		{
			FWModifyDCLJump (pDeviceDescription->pGlobalDVOutData->DCLProgramID,
							 pDVPlayBufferGroupData->pEndOfBufferGroupDCLJump,
							 pDeviceDescription->pGlobalDVOutData->pUnderrunDCLLabel);
		}

		pDVPlayBufferGroupData = pDVPlayBufferGroupData->pNextLocalData;
	}

	// Set up params for starting isochronous channel.
	isochChannelCommandObjectID = pDeviceDescription->pGlobalDVOutData->isochChannelCommandObjectID;
	FWSetFWCommandParams (isochChannelCommandObjectID, (FWReferenceID) kInvalidFWReferenceID, 0, nil, 0);
	FWSetIsochChannelCommandIsochChannelID (isochChannelCommandObjectID, pDeviceDescription->outputIsochChannelID);

	// Restart the isochronous channel.
	FWStartIsochronousChannel (isochChannelCommandObjectID);
}



///////////////////////////////////////////////////////////////////////
//
// DVDisposeDCLOutput
//
///////////////////////////////////////////////////////////////////////
OSErr disposeDCLOutput( DVGlobalOutPtr pOutputData )
{
	DVLocalOutPtr				pLocalData,
								pNextLocalData;
	UInt32						bufferGroupNum;
	OSErr						error = noErr;

	if( pOutputData != nil )
	{
		// Deallocate play buffer group data records.
		// and update lists associated with them
		pLocalData = pOutputData->pBufferGroupDataList;
		for (bufferGroupNum = 0; bufferGroupNum < kNumPlayBufferGroups; bufferGroupNum++)
		{
			if( pLocalData != nil )
			{	
				pNextLocalData = pLocalData->pNextLocalData;
				dclDeallocatePlayBufferGroup (pLocalData);
				pLocalData = pNextLocalData;
			}
		}
			
		if( pOutputData->pDCLCommandPool != nil )	
			dclDeallocateCommandPool( pOutputData->pDCLCommandPool );
		
		if( pOutputData->pDVFrameOutputData != nil )
			releaseDVFrameIO( pOutputData->pDVFrameOutputData );
		
		FWDisposeDCLProgram( pOutputData->DCLProgramID );
		
		if( pOutputData->pTransmitBuffers != nil )
			PoolDeallocate ( pOutputData->pTransmitBuffers );
		
		if( pOutputData->isochChannelCommandObjectID != nil )
			FWDeallocateFWCommandObject ( pOutputData->isochChannelCommandObjectID );
		
		PoolDeallocate ( pOutputData );
	}
	return( error );
}


//////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////
// Utilitiy Routines
//////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////



///////////////////////////////////////////////////////////////////////////////////
// Buffer Allocation Routines
//   For Output
///////////////////

////////////////////////////////////////////////////////////////////////////////
//
// DVCAllocatePlayBufferGroup
//
//   This routine allocates a buffer group for playing.
//

DVLocalOutPtr dclAllocatePlayBufferGroup(DVGlobalOutPtr pGlobalData)
{
	DVLocalOutPtr				pLocalData = nil,
								pPrevLocalData,
								pNextLocalData;
	OSErr						error = noErr;

	// Allocate buffer group data record.
	pLocalData = (DVLocalOutPtr) PoolAllocateResident(sizeof (DVLocalOut), true);
	if (pLocalData != nil)
		pLocalData->pGlobalData = pGlobalData;
	else
		error = memFullErr;

	// Insert buffer group data record into list.
	if (error == noErr)
	{
		pNextLocalData = pGlobalData->pBufferGroupDataList;
		if (pNextLocalData != nil)
		{
			pPrevLocalData = pNextLocalData->pPrevLocalData;

			pNextLocalData->pPrevLocalData = pLocalData;
			pLocalData->pNextLocalData = pNextLocalData;
		}
		else
		{
			pPrevLocalData = pLocalData;

			pGlobalData->pBufferGroupDataList = pLocalData;
		}

		pPrevLocalData->pNextLocalData = pLocalData;
		pLocalData->pPrevLocalData = pPrevLocalData;
	}

	return pLocalData;
}


////////////////////////////////////////////////////////////////////////////////
//
// DVDeallocatePlayBufferGroup
//
//   This routine deallocates a buffer group for playing.
//

void dclDeallocatePlayBufferGroup( DVLocalOutPtr	pLocalData )
{
	if (pLocalData != nil)
	{
		if (pLocalData->bufferGroupUpdateDCLList != nil)
			PoolDeallocate ((Ptr) pLocalData->bufferGroupUpdateDCLList);

		PoolDeallocate ( pLocalData );
	}
}


////////////////////////////////////////////////////////////////////////////////
//
// DVCreatePlayBufferGroupUpdateList
//
//   This routine creates the update list for a play buffer group.
//

OSErr createDVPlayBufferGroupUpdateList( DVLocalOutPtr pLocalData)
{
	DCLCommandPtr				pDCLCommand,
								pLastDCLCommand;
	DCLCommandPtr				*updateDCLList,
								*pUpdateDCLListEntry;
	UInt32						opcode;
	UInt32						updateListSize;
	OSErr						error = noErr;

	// Loop through all DCL commands in buffer group and count all send packet DCL
	// commands.
	pDCLCommand = pLocalData->pFirstBufferGroupDCLCommand;
	pLastDCLCommand = pLocalData->pLastBufferGroupDCLCommand;
	updateListSize = 0;
	while (pDCLCommand != pLastDCLCommand)
	{
		opcode = pDCLCommand->opcode & ~kFWDCLOpFlagMask;
		if ((opcode == kDCLSendPacketStartOp) || (opcode == kDCLSendPacketOp))
			updateListSize++;

		pDCLCommand = pDCLCommand->pNextDCLCommand;
	}
	opcode = pDCLCommand->opcode & ~kFWDCLOpFlagMask;
	if ((opcode == kDCLSendPacketStartOp) || (opcode == kDCLSendPacketOp))
		updateListSize++;

	// Allocate update list.
	updateDCLList = (DCLCommandPtr *)
		PoolAllocateResident (updateListSize * sizeof (DCLCommandPtr), false);
	if (updateDCLList == nil)
		error = memFullErr;

	// Loop through all DCL commands in buffer group and add all send packet DCL
	// commands to update list.
	if (error == noErr)
	{
		pDCLCommand = pLocalData->pFirstBufferGroupDCLCommand;
		pLastDCLCommand = pLocalData->pLastBufferGroupDCLCommand;
		pUpdateDCLListEntry = updateDCLList;

		while (pDCLCommand != pLastDCLCommand)
		{
			opcode = pDCLCommand->opcode & ~kFWDCLOpFlagMask;
			if ((opcode == kDCLSendPacketStartOp) || (opcode == kDCLSendPacketOp))
				*pUpdateDCLListEntry++ = pDCLCommand;

			pDCLCommand = pDCLCommand->pNextDCLCommand;
		}

		opcode = pDCLCommand->opcode & ~kFWDCLOpFlagMask;
		if ((opcode == kDCLSendPacketStartOp) || (opcode == kDCLSendPacketOp))
			*pUpdateDCLListEntry++ = pDCLCommand;
	}

	// Save update list.
	if (error == noErr)
	{
		pLocalData->bufferGroupUpdateDCLList = updateDCLList;
		pLocalData->updateListSize = updateListSize;
	}
	else
	{
		pLocalData->bufferGroupUpdateDCLList = nil;
		pLocalData->updateListSize = 0;
	}

	return ( error );
}


////////////////////////////////////////////////////////////////////////////////////
// DCL Allocation Routines
//
//  We run our own allocation engine because of the sheer number of DCLs. The command
// pool holds larger blocks out of which we allocate the DCL commands, this takes
// some work out of the hands of the memory manager.
//
///////////////////

////////////////////////////////////////////////////////////////////////////////
//
// AllocateDCLCommand
//
//   This routine allocates a DCL command.
//

DCLCommandPtr dclAllocateCommand(DCLCommandPoolPtr pDCLCommandPool, UInt32 dclSize)
{
	DCLCommandBlockPtr			pDCLCommandBlock;
	DCLCommandPtr				pDCLCommand;

	// Get last DCL command block in pool.
	pDCLCommandBlock = pDCLCommandPool->pLastDCLCommandBlock;

	// Check if we have enough room in command block.  Allocate a new block if there
	// is not enough room.
	if (pDCLCommandBlock != nil)
	{
		if( dclSize > (kDCLCommandPoolBlockSize - pDCLCommandBlock->blockLevel) )
			pDCLCommandBlock = dclAllocateCommandBlock( pDCLCommandPool );
	}
	else
	{
		pDCLCommandBlock = dclAllocateCommandBlock ( pDCLCommandPool );
	}

	// Allocate a DCL from the command block.
	if (pDCLCommandBlock != nil)
	{
		pDCLCommand =
			(DCLCommandPtr) (pDCLCommandBlock->block + pDCLCommandBlock->blockLevel);
		pDCLCommandBlock->blockLevel += dclSize;
	}
	else
	{
		pDCLCommand = nil;
	}

	return pDCLCommand;
}


////////////////////////////////////////////////////////////////////////////////
//
// AllocateDCLCommandBlock
//
//   This routine allocates a DCL command block.
//

DCLCommandBlockPtr dclAllocateCommandBlock( DCLCommandPoolPtr pDCLCommandPool )
{
	DCLCommandBlockPtr			pDCLCommandBlock = nil;
	Ptr							block;
	OSStatus					error = noErr;

	// Allocate command block record.
	pDCLCommandBlock =
		(DCLCommandBlockPtr) PoolAllocateResident( sizeof(DCLCommandBlock), true );
	if( pDCLCommandBlock == nil )
		error = memFullErr;

	// Allocate command block block.
	if( error == noErr )
	{
		block = PoolAllocateResident( kDCLCommandPoolBlockSize, false );
		if (block != nil)
			pDCLCommandBlock->block = block;
		else
			error = memFullErr;
	}

	// Insert command block into pool.
	if( error == noErr )
	{
		if( pDCLCommandPool->pFirstDCLCommandBlock != nil )
			pDCLCommandPool->pLastDCLCommandBlock->pNextDCLCommandBlock = pDCLCommandBlock;
		else
			pDCLCommandPool->pFirstDCLCommandBlock = pDCLCommandBlock;

		pDCLCommandPool->pLastDCLCommandBlock = pDCLCommandBlock;
		pDCLCommandBlock->pNextDCLCommandBlock = nil;
	}

	// Clean up on error.
	if( ( error != noErr ) && ( pDCLCommandBlock != nil ) )
	{
		dclDeallocateCommandBlock( pDCLCommandBlock );
		pDCLCommandBlock = nil;
	}

	// Return results.
	return( pDCLCommandBlock );
}


////////////////////////////////////////////////////////////////////////////////
//
// DeallocateDCLCommandBlock
//
//   This routine deallocates a DCL command block.
//

void dclDeallocateCommandBlock( DCLCommandBlockPtr pDCLCommandBlock )
{
	if( pDCLCommandBlock != nil )
	{
		// Deallocate command block block.
		if( pDCLCommandBlock->block != nil )
			PoolDeallocate( pDCLCommandBlock->block );

		// Deallocate command block record.
		PoolDeallocate( (Ptr) pDCLCommandBlock );
		
		//zzz if there were any errors...?
	}
}

////////////////////////////////////////////////////////////////////////////////
//
// AllocateDCLCommandPool
//
//   This routine allocates a DCL command pool.
//

DCLCommandPoolPtr dclAllocateCommandPool( void )
{
	DCLCommandPoolPtr			pDCLCommandPool;

	// Allocate DCL command pool record.
	pDCLCommandPool = (DCLCommandPoolPtr) PoolAllocateResident( sizeof( DCLCommandPool ), true );

	return (pDCLCommandPool);
}

////////////////////////////////////////////////////////////////////////////////
//
// DeallocateDCLCommandPool
//
//   This routine deallocates a DCL command pool.
//

void dclDeallocateCommandPool( DCLCommandPoolPtr pDCLCommandPool )
{
	DCLCommandBlockPtr			pDCLCommandBlock,
								pNextDCLCommandBlock;

	if( pDCLCommandPool != nil )
	{
		// Deallocate all command blocks.
		pDCLCommandBlock = pDCLCommandPool->pFirstDCLCommandBlock;
		while( pDCLCommandBlock != nil )
		{
			pNextDCLCommandBlock = pDCLCommandBlock->pNextDCLCommandBlock;
			dclDeallocateCommandBlock( pDCLCommandBlock );
			pDCLCommandBlock = pNextDCLCommandBlock;
		}

		// Deallocate command pool record.
		PoolDeallocate ((Ptr) pDCLCommandPool);
		//zzz if there was an error...?
	}
}


////////////////////////////////////////////////////////////////////////////////
//
// AddFWCycleTimeToFWCycleTime
//
//   This routine adds the two given cycle times.
//

UInt32	addFWCycleTimeToFWCycleTime( UInt32 cycleTime1, UInt32 cycleTime2 )
{
	UInt32						secondCount,
								cycleCount,
								cycleOffset;
	UInt32						cycleTime;

	// Add cycle offsets.
	cycleOffset = (cycleTime1 & 0x0FFF) + (cycleTime2 & 0x0FFF);

	// Add cycle counts.
	cycleCount = (cycleTime1 & 0x01FFF000) + (cycleTime2 & 0x01FFF000);

	// Add any carry over from cycle offset to cycle count.
	if (cycleOffset > 3071)
	{
		cycleCount += 0x1000;
		cycleOffset -= 3072;
	}

	// Add secondCounts.
	secondCount = (cycleTime1 & 0xFE000000) + (cycleTime2 & 0xFE000000);

	// Add any carry over from cycle count to secondCount.
	if (cycleCount > (7999 << 12))
	{
		secondCount += 0x02000000;
		cycleCount -= (8000 << 12);
	}

	// Put everything together into cycle time.
	cycleTime = secondCount | cycleCount | cycleOffset;

	return (cycleTime);
}


////////////////////////////////////////////////////////////////////////////////
//
// SubtractFWCycleTimeFromFWCycleTime
//
//   This routine subtracts the two given cycle times.
//

UInt32 subtractFWCycleTimeFromFWCycleTime( UInt32 cycleTime1, UInt32 cycleTime2 )
{
	SInt32						secondCount,
								cycleCount,
								cycleOffset;
	UInt32						cycleTime;

	// Subtract cycle offsets.
	cycleOffset = (cycleTime1 & 0x0FFF) - (cycleTime2 & 0x0FFF);

	// Subtract cycle counts.
	cycleCount = (cycleTime1 & 0x01FFF000) - (cycleTime2 & 0x01FFF000);

	// Subtract any borrow over from cycle offset to cycle count.
	
	if (cycleOffset < 0)
	{
		cycleCount -= 0x1000;
		cycleOffset += 3072;
	}

	// Subtract secondCounts.
	secondCount = (cycleTime1 & 0xFE000000) - (cycleTime2 & 0xFE000000);

	// Subtract any borrow over from cycle count to secondCount.
	if (cycleCount < 0)
	{
		secondCount -= 0x02000000;
		cycleCount += (8000 << 12);
	}

	// Put everything together into cycle time.
	cycleTime = secondCount | cycleCount | cycleOffset;

	return (cycleTime);
}


////////////////////////////////////////////////////////////////////////////////
//
// ConvertFractionalSecondsToFWCycleTime
//
//   This routine converts a time represented in fractional seconds to a time
// represented in cycle timer format.
//

UInt32	convertFractionalSecondsToFWCycleTime( UInt32 secondsNumerator, UInt32 secondsDenominator )
{
	float						fSecondCount;
	float						fCycleCount;
	float						fCycleOffset;
	UInt32						iSecondsCount;
	UInt32						iCycleCount;
	UInt32						iCycleOffset;
	UInt32						secondsCycleTime;

	// Convert fractional seconds into floating point and compute seconds count.
	fSecondCount = ((float) secondsNumerator) / ((float) secondsDenominator);
	iSecondsCount = (UInt32) fSecondCount;

	// Subtract whole seconds out of fSecondCount and convert to cycle count.
	fCycleCount = (fSecondCount - ((float) iSecondsCount)) * 8000.0;
	iCycleCount = (UInt32) fCycleCount;

	// Subtract whole cycles out of fCycleCount and convert to cycle offset.
	fCycleOffset = (fCycleCount - ((float) iCycleCount)) * 3072.0;
	iCycleOffset = (UInt32) fCycleOffset;

	// Convert to cycle timer format.
	secondsCycleTime = (iSecondsCount << 25) | (iCycleCount << 12) | iCycleOffset;

	return (secondsCycleTime);
}

//////////////////////////////////////////////////////////////////////
// DVGetNextFullOutputFrame
//
// Returns next frame full of data for output
//

OSErr getNextFullOutputFrame( DVIODataPtr pOData, UInt32** ppFrame )
{
	OSErr						error = noErr;
	QElemPtr					pQElem;
	
	// dequeue an element from the output list
	error = PBDequeueFirst( pOData->pFullQ, &pQElem);

	// if we got a frame
	if( error == noErr )
	{
		// put old frame on empty queue
		error = PBEnqueueLast( pOData->pCurFrameElem, pOData->pEmptyQ );
		//zzz what if we can't enqueue
		
		// save new frame element
		pOData->pCurFrameElem = pQElem;
		
		// return the actual frame
		*ppFrame = ((DVFrameQElemPtr)pQElem)->pFrame;
	}
	else
		// return old frame
		*ppFrame = ((DVFrameQElemPtr)pOData->pCurFrameElem)->pFrame;
	
	return( error );
}

///////////////////////////////////////////////////////////////////////
//
// DVGetNextEmptyOutputFrame
//
///////////////////////////////////////////////////////////////////////

OSErr getNextEmptyOutputFrame( DVIODataPtr pOData, UInt32** ppFrame)
{
	OSErr						error = noErr;
	DVFrameQElemPtr				pQElem;

	// dequeue an element from the empty list
	error = PBDequeueFirst( pOData->pEmptyQ, (QElemPtr *) &pQElem );
			
	// if we got an element
	if( error == noErr )
	{
		// put the frame on the busy queue
		error = PBEnqueueLast( (QElemPtr) pQElem, pOData->pBusyQ );
		//zzz and if we get an error
		
		// return the frame
		*ppFrame = pQElem->pFrame; 
	}
	else
		*ppFrame = nil;
	
	return( error );
}

///////////////////////////////////////////////////////////////////////
//
// DVQueueNextFullOutputFrame
//
///////////////////////////////////////////////////////////////////////

OSErr queueNextFullOutputFrame( DVIODataPtr pOData, UInt32* pFrame )
{
	OSStatus					error = noErr;
	QElemPtr					pCurElem;
		
	// find frame in busy queue
	pCurElem = pOData->pBusyQ->qHead;
	while( pCurElem != nil)
	{
		// is it the one we want?
		if( ((DVFrameQElemPtr)pCurElem)->pFrame == pFrame )
			break;
		else
			// next
			pCurElem = pCurElem->qLink;
	}
	
	// if we found it	
	if( pCurElem != nil )
	{
		// get the element from the busy queue
		error = PBDequeue( pCurElem, pOData->pBusyQ );
		
		// and put it on the full queue
		if( error == noErr ) 
			error = PBEnqueueLast( pCurElem, pOData->pFullQ );	
	}
	else
		error = kDVFrameNotFoundErr;
		
	return( error );
}

//////////////////////////////////////////////////////////////////////
// DVGetNextEmptyInputFrame
//
// Returns next empty frame for input
//

OSErr getNextEmptyInputFrame( DVIODataPtr pIData, UInt32** ppFrame )
{
	OSErr						error = noErr;
	QElemPtr					pQElem;
	
	// dequeue an element from the output list
	error = PBDequeueFirst( pIData->pEmptyQ, &pQElem);

	if( error != noErr )
	{
		error = PBDequeueFirst( pIData->pFullQ, &pQElem);
	}
	
	// if we got a frame
	if( error == noErr )
	{
		// put old frame on full queue
		error = PBEnqueueLast( pIData->pCurFrameElem, pIData->pFullQ );
		//zzz what if we can't enqueue
		
		// save new frame element
		pIData->pCurFrameElem = pQElem;
		
		// return the actual frame
		*ppFrame = ((DVFrameQElemPtr)pQElem)->pFrame;
	}
	else
		// return old frame
		*ppFrame = ((DVFrameQElemPtr)pIData->pCurFrameElem)->pFrame;
	
	return( error );
}

//////////////////////////////////////////////////////////////////////
//
// DVGetNextFullInputFrame
//
// Returns next full frame of data for input
//

OSErr getNextFullInputFrame( DVIODataPtr pIData, UInt32** ppFrame )
{
	OSErr						error = noErr;
	DVFrameQElemPtr				pQElem;
	
	// dequeue an element from the full list
	error = PBDequeueFirst( pIData->pFullQ, (QElemPtr *) &pQElem );
			
	// if we got an element
	if( error == noErr )
	{
		// put the frame on the busy queue
		error = PBEnqueueLast( (QElemPtr) pQElem, pIData->pBusyQ );
		//zzz and if we get an error
		
		// return the frame
		*ppFrame = pQElem->pFrame; 
	}
	else
		*ppFrame = nil;
	
	return( error );
}

///////////////////////////////////////////////////////////////////////
//
// DVReleaseFullInputFrame
//
///////////////////////////////////////////////////////////////////////

OSErr releaseFullInputFrame( DVIODataPtr pIData, UInt32* pFrame )
{
	OSStatus					error = noErr;
	QElemPtr					pCurElem;
		
	// find frame in busy queue
	pCurElem = pIData->pBusyQ->qHead;
	while( pCurElem != nil)
	{
		// is it the one we want?
		if( ((DVFrameQElemPtr)pCurElem)->pFrame == pFrame )
			break;
		else
			// next
			pCurElem = pCurElem->qLink;
	}
	
	// if we found it	
	if( pCurElem != nil )
	{
		// get the element from the busy queue
		error = PBDequeue( pCurElem, pIData->pBusyQ );
		
		// and put it on the empty queue
		if( error == noErr ) 
			error = PBEnqueueLast( pCurElem, pIData->pEmptyQ );	
	}
	else
		error = kDVFrameNotFoundErr;
		
	return( error );
}

//////////////////////////////////////////////////////////////////////
//
// DVGetCurrentDCLFrame
//

Boolean isDVFinished( DVIODataPtr pIOData )
{
	Boolean		result = false;
	
	if( pIOData != nil )
	{
		if( pIOData->pFullQ->qHead == nil )
			result = true;
		else
			result = false;
	}
	else
		result = false;
			
	return( result );
}

//////////////////////////////////////////////////////////////////////
//
// DVGetCurrentDCLFrame
//

OSErr getCurrentDCLFrame( DVIODataPtr pIOData, UInt32** ppFrame )
{
	OSErr						error = noErr;

	if( pIOData != nil )
	{
		// get the current frame
		*ppFrame = ((DVFrameQElemPtr)pIOData->pCurFrameElem)->pFrame;
	}
	else
	{
		error = kDVFrameNotFoundErr;
		*ppFrame = nil;
	}	
	return( error );
}

///////////////////////////////////////////////////////////////////////
//
// DVInitFrameIO
//

OSErr initDVFrameInput(UInt32 standard, DVIODataPtr* ppIOData)
{
	OSErr				error = noErr;
	DVIODataPtr			pIOData;
	DVFrameQElemPtr		pQElem[kNumInputDVFrames];
	UInt32				i;
	UInt32				frameSize;
	
	// create output data struct
	pIOData = (DVIODataPtr) PoolAllocateResident( sizeof( DVIOData ), true );

	if( pIOData == nil )
	{
		error = memFullErr;
		goto bail;
	}
	
	// create busy queue
	error = PBQueueCreate( &(pIOData->pBusyQ) );
	if( error != noErr )
		goto bail;
		
	error = PBQueueInit( pIOData->pBusyQ );
	if( error != noErr )
		goto bail;
				
	// create empty queue
	error = PBQueueCreate( &(pIOData->pEmptyQ) );
	if( error != noErr )
		goto bail;
			
	error = PBQueueInit( pIOData->pEmptyQ );
	if( error != noErr )
		goto bail;
	
	
	// create full queue
	error = PBQueueCreate( &(pIOData->pFullQ) );
	if( error != noErr )
		goto bail;
			
	error = PBQueueInit( pIOData->pFullQ );
	if( error != noErr )
		goto bail;
				
	for(i = 0; i < kNumInputDVFrames; i++)
	{
		// create queue elements
		pQElem[i] = (DVFrameQElemPtr) PoolAllocateResident( sizeof(DVFrameQElem), true );
		if ( pQElem[i] == nil )
		{
			error = memFullErr;
			goto bail;
		}
		
		// create frames
		if (standard == ntscIn)
			frameSize = kNTSCCompressedBufferSize;
		else
			frameSize = kPALCompressedBufferSize;
			
		pQElem[i]->pFrame = (UInt32 *) PoolAllocateResident( frameSize, true );
		if ( pQElem[i]->pFrame == nil )
		{
			error = memFullErr;
			goto bail;
		}
	}
	
	// set current frame to start
	pIOData->pCurFrameElem = (QElemPtr) pQElem[0];
	
	// insert the rest of the queue elements into empty queue
	for(i = 1; i < kNumInputDVFrames; i++ )
	{
		error = PBEnqueueLast( (QElemPtr) pQElem[i], pIOData->pEmptyQ );
	}

bail:
	if( error != noErr )
	{
		releaseDVFrameIO( pIOData );
		pIOData = nil;
	}

	*ppIOData = pIOData;
		
	return( error );
}

///////////////////////////////////////////////////////////////////////
//
// initDVFrameOutput
//
OSErr initDVFrameOutput(UInt32 standard, DVIODataPtr* ppIOData)
{
	OSErr				error = noErr;
	DVIODataPtr			pIOData;
	DVFrameQElemPtr		pQElem[kNumOutputDVFrames];
	UInt32				i;
	UInt32				frameSize;
	
	// create output data struct
	pIOData = (DVIODataPtr) PoolAllocateResident(sizeof(DVIOData), true);

	if( pIOData == nil )
	{
		error = memFullErr;
		goto bail;
	}
	
	// create busy queue
	error = PBQueueCreate( &(pIOData->pBusyQ) );
	if( error != noErr )
		goto bail;
		
	error = PBQueueInit( pIOData->pBusyQ );
	if( error != noErr )
		goto bail;
				
	// create empty queue
	error = PBQueueCreate( &(pIOData->pEmptyQ) );
	if( error != noErr )
		goto bail;
			
	error = PBQueueInit( pIOData->pEmptyQ );
	if( error != noErr )
		goto bail;
	
	// create full queue
	error = PBQueueCreate( &(pIOData->pFullQ) );
	if( error != noErr )
		goto bail;
			
	error = PBQueueInit( pIOData->pFullQ );
	if( error != noErr )
		goto bail;
				
	for(i = 0; i < kNumOutputDVFrames; i++)
	{
		// create queue elements
		pQElem[i] = (DVFrameQElemPtr) PoolAllocateResident( sizeof(DVFrameQElem), true );
		if ( pQElem[i] == nil )
		{
			error = memFullErr;
			goto bail;
		}
		
		// create frames
		if (standard == ntscIn)
			frameSize = kNTSCCompressedBufferSize;
		else
			frameSize = kPALCompressedBufferSize;
			
		pQElem[i]->pFrame = (UInt32 *) PoolAllocateResident( frameSize, true );
		if ( pQElem[i]->pFrame == nil )
		{
			error = memFullErr;
			goto bail;
		}
	}
	
	// set current frame to start
	pIOData->pCurFrameElem = (QElemPtr) pQElem[0];
	
	// insert the rest of the queue elements into empty queue
	for(i = 1; i < kNumOutputDVFrames; i++ )
	{
		error = PBEnqueueLast( (QElemPtr) pQElem[i], pIOData->pEmptyQ );
	}

bail:
	if( error != noErr )
	{
		releaseDVFrameIO( pIOData );
		pIOData = nil;
	}

	*ppIOData = pIOData;
		
	return( error );
}

///////////////////////////////////////////////////////////////////////
//
// DVDeleteFrameAndElement
//
///////////////////////////////////////////////////////////////////////

static void deleteDVFrameAndElement( DVFrameQElemPtr pQElem )
{
	// deallocate frame
	// checks just to be safe
	if( pQElem != nil )
	{
		if( pQElem->pFrame != nil )
			PoolDeallocate( pQElem->pFrame );
	
		// deallocate queue element
		PoolDeallocate( pQElem );
		
		//zzz never check errors
	}
}

///////////////////////////////////////////////////////////////////////
//
// DVReleaseFrameIO
//
///////////////////////////////////////////////////////////////////////

OSErr releaseDVFrameIO( DVIODataPtr pIOData )
{
	OSErr						error = noErr;
	QElemPtr					pQElem;
	
	if( pIOData != nil )
	{
		if( pIOData->pBusyQ != nil )
		{
			// dequeue and delete all elements in the busy queue
			do {
				error = PBDequeueFirst( pIOData->pBusyQ, (QElemPtr *) &pQElem );
			
				if( error == noErr )
					deleteDVFrameAndElement( (DVFrameQElemPtr) pQElem );
					
			} while( error == noErr );
			
			error = PBQueueDelete( pIOData->pBusyQ );
		}
		
		
		if( pIOData->pEmptyQ != nil )
		{
			// dequeue and delete all elements in the empty queue
			do {
				error = PBDequeueFirst( pIOData->pEmptyQ, (QElemPtr *) &pQElem );
			
				if( error == noErr )
					deleteDVFrameAndElement( (DVFrameQElemPtr) pQElem );
					
			} while( error == noErr );	
			
			error = PBQueueDelete( pIOData->pEmptyQ );
		}

		
		if( pIOData->pFullQ != nil )
		{	
			// dequeue and delete all elements in the full queue
			do {
				error = PBDequeueFirst( pIOData->pFullQ, (QElemPtr *) &pQElem );
			
				if( error == noErr )
					deleteDVFrameAndElement( (DVFrameQElemPtr) pQElem );
					
			} while( error == noErr );	
			
			error = PBQueueDelete( pIOData->pFullQ );
		}
		
		// delete current frame and element
		if( pIOData->pCurFrameElem != nil )
			deleteDVFrameAndElement( (DVFrameQElemPtr) pIOData->pCurFrameElem );
		
		// dump the main guy
		PoolDeallocate( pIOData );
	}
		
	return( error );
}


///////////////////////////////////////////////////////////////////////
//
// DVInitDCLInput
//
///////////////////////////////////////////////////////////////////////

OSErr dclInitInput(DeviceDescriptionPtr pDeviceDescription,
		IsochComponentGlobals* isochComponentGlobals )
{
	Ptr							pReceiveBuffer;
	Ptr							pDCLCommand;
	DVGlobalInPtr				pGlobalData;
	DCLLabelPtr					pStartDCLLabel;
	DVLocalInPtr				pLocalData;
	DCLCommandPtr				*updateDCLList,
								*startUpdateDCLList;
	DCLUpdateDCLListPtr			pDCLUpdateDCLList;
	DCLTransferPacketPtr		pDCLTransferPacket;
	DCLCallProcPtr				pDCLCallProc;
	DCLJumpPtr					pDCLInputLoop;
	UInt32						packetNum;
	UInt32						bufferNum;
	UInt32						updateListSize;
	OSErr						error = noErr;
	
	// Create the global data structure
	pGlobalData = PoolAllocateResident( sizeof(DVGlobalIn), true );
	if( pGlobalData == nil )
		error = memFullErr;
	else
		pDeviceDescription->pGlobalDVInData = pGlobalData;

	pGlobalData->isochComponentGlobals = isochComponentGlobals;
	pGlobalData->deviceID = pDeviceDescription->deviceID;
	pGlobalData->pDCLList = nil;
	pGlobalData->ppUpdateDCLList = nil;
	pGlobalData->pDVFrameInputData = nil;
	
	// Create input buffer. Add slop for page alignment
	pGlobalData->pReceiveBuffers = PoolAllocateResident(kInputBufferSize + 4096, false);
	if( pGlobalData->pReceiveBuffers == nil )
	{
		error = memFullErr;
		goto bail;
	}
	pReceiveBuffer = pGlobalData->pReceiveBuffers;

	// Align buffer, should be aligned already
	pReceiveBuffer = (Ptr) ((((UInt32) pReceiveBuffer) + 4096) & 0xfffff000);
	
	#ifdef dbg_in_init
		ASSERTVAR( error, "Error initializing input DCL data structures.  Error: %d" );
	#endif
	
	// init frame IO
	error = initDVFrameInput(pDeviceDescription->standard, &pGlobalData->pDVFrameInputData);
	if( error != noErr )
		goto bail;
		
	error = getCurrentDCLFrame( pGlobalData->pDVFrameInputData, (UInt32 **) &(pGlobalData->pImageBuffer) );		
	if( error != noErr )
		goto bail;
	
	#ifdef dbg_in_init
		ASSERTVAR( error, "Error initializing input DCL frame I/O system.  Error: %d" );
	#endif
	
	// Fill out global structure
	pGlobalData->isNTSC = (pDeviceDescription->standard == ntscIn);
	pGlobalData->packetCount = 0;
	
	error = FWCreateDCLProgram( &(pGlobalData->DCLProgramID) );
	pDeviceDescription->dclProgramID = pGlobalData->DCLProgramID;
	if( error != noErr )
		goto bail;
		
	// create list of DCLs for channel
	// make sure we zero it so deallocation can work even if not fully initialized
	pGlobalData->pDCLList = (DCLCommandPtr) PoolAllocateResident( kDCLReadProgramSize, true );
	if ( pGlobalData->pDCLList == nil )
	{
		error = memFullErr;
		goto bail;
	}

	// create list of update DCLs for channel
	// make sure we zero it so deallocation can work even if not fully initialized
	pGlobalData->ppUpdateDCLList = (DCLCommandPtr *) PoolAllocateResident( kNumInputDCLs * sizeof (DCLCommandPtr), true );
	if ( pGlobalData->ppUpdateDCLList == nil )
	{
		error = memFullErr;
		goto bail;
	}

	#ifdef dbg_in_init
		DEBUGVAR( "Initialized input DCL commands.  Error: %d", error );
	#endif
	
	// Get pointer to start of DCL commands and update list.
	pDCLCommand = (Ptr) pGlobalData->pDCLList;
	updateDCLList = pGlobalData->ppUpdateDCLList;

	// Create label for start of loop.
	pStartDCLLabel = (DCLLabelPtr) pDCLCommand;
	pDCLCommand += sizeof( DCLLabel );
	pStartDCLLabel->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
	pStartDCLLabel->opcode = kDCLLabelOp;


	// Create input buffer lists of 100 packets each.
	for (bufferNum = 0; bufferNum < kNumInputBuffers; bufferNum++)
	{
		// Create the DCL input record record and fill it in.
		pLocalData = (DVLocalInPtr) PoolAllocateResident( sizeof(DVLocalIn), true );
		if( pLocalData == nil )
		{
			error = memFullErr;
			goto bail;
		}
		pLocalData->pFirstCmd = (DCLCommandPtr) pDCLCommand;
		pLocalData->pGlobalData = pGlobalData;
	
		startUpdateDCLList = updateDCLList;
		updateListSize = 0;
		// Create transfer DCL for each packet.
		for (packetNum = 0; packetNum < kNumPacketsPerInputBuffer; packetNum++)
		{
			// Receive one packet up to kReceiveDVPacketSize bytes.
			pDCLTransferPacket = (DCLTransferPacketPtr) pDCLCommand;
			pDCLCommand += sizeof (DCLTransferPacket);
			pDCLTransferPacket->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
			pDCLTransferPacket->opcode = kDCLReceivePacketStartOp;
			pDCLTransferPacket->buffer = pReceiveBuffer;
			pDCLTransferPacket->size = kReceiveDVPacketSize;

			*updateDCLList++ = (DCLCommandPtr) pDCLTransferPacket;
			updateListSize++;
			pReceiveBuffer += kAlignedDVPacketSize;
		}

		// Create update DCL list.
		pDCLUpdateDCLList = (DCLUpdateDCLListPtr) pDCLCommand;
		pDCLCommand += sizeof (DCLUpdateDCLList);
		pDCLUpdateDCLList->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
		pDCLUpdateDCLList->opcode = kDCLUpdateDCLListOp;
		pDCLUpdateDCLList->dclCommandList = startUpdateDCLList;
		pDCLUpdateDCLList->numDCLCommands = updateListSize;

		// Call the DVStorePackets proc.
		pDCLCallProc = (DCLCallProcPtr) pDCLCommand;
		pDCLCommand += sizeof (DCLCallProc);
		pDCLCallProc->pNextDCLCommand = (DCLCommandPtr) pDCLCommand;
		pDCLCallProc->opcode = kDCLCallProcOp;
		pDCLCallProc->proc = storeDVPackets;
		pDCLCallProc->procData = (UInt32) pLocalData;
	}
	
	
	#ifdef dbg_in_init
		DEBUGVAR( "Created input DCL program.  Error: %d", error );
	#endif
	
	// Loop to start of ping pong.
	pDCLInputLoop = (DCLJumpPtr) pDCLCommand;
	pDCLInputLoop->pNextDCLCommand = nil;
	pDCLInputLoop->opcode = kDCLJumpOp;
	pDCLInputLoop->pJumpDCLLabel = pStartDCLLabel;
			
	// Set start of DCL program.
	error = FWSetDCLProgramStart (pGlobalData->DCLProgramID, pGlobalData->pDCLList);

bail:	
	if( error != noErr )
	{
		disposeDCLInput( pGlobalData );
		pGlobalData = nil;
	}
	
	return( error );
}

///////////////////////////////////////////////////////////////////////
//
// DVStorePackets(
//
///////////////////////////////////////////////////////////////////////

void storeDVPackets( DCLCommandPtr pDCLCommandPtr )
{
	DCLCallProcPtr				pDCLCallProc;
	DVLocalInPtr				pLocalData;
	DVGlobalInPtr				pGlobalData;
	DCLCommandPtr				pCurrentCmd;
	DCLTransferPacketPtr		pDCLTransferPacket;
	Ptr							pPacketBuffer;
	UInt32						packetHeader, packetSize, packetNum, packetPerFrame;
	Boolean						vSyncDetected;
	UInt8						currentSequenceCount;
	
	// Recast DCL command.
	pDCLCallProc = (DCLCallProcPtr) pDCLCommandPtr;

	// Get input data.
	pLocalData = (DVLocalInPtr) pDCLCallProc->procData;
	pGlobalData = pLocalData->pGlobalData;
	
	// Get info from ping pong data.
	pCurrentCmd = pLocalData->pFirstCmd;
	
	// How many packets we talkin'?
	packetPerFrame = pGlobalData->isNTSC ? kNumPacketsPerNTSCFrame : kNumPacketsPerPALFrame;
	
	for ( packetNum = 0; packetNum < kNumPacketsPerInputBuffer; packetNum++ )
	{
		// compute size of transfer
		pDCLTransferPacket = (DCLTransferPacketPtr) pCurrentCmd;
		pPacketBuffer = pDCLTransferPacket->buffer;
		packetHeader = *((UInt32*) pPacketBuffer);
		pPacketBuffer += 4; // 4 byte 1394 header
		packetSize = (packetHeader & kFWIsochDataLength) >> kFWIsochDataLengthPhase;

		// detect vSync
		if( packetSize > 8 )
			vSyncDetected = ((*(short *)(pPacketBuffer + 8)  & 0xE0F8 ) == 0x0000 );
		else
			vSyncDetected = false;
	
		// get current data block sequence counter value and increment saved value
		// saved value will be decremented at the end for null packets which do not
		// increment data block count
		currentSequenceCount = pPacketBuffer[3];
		pGlobalData->lastSequenceCount++;

		// skip over CIP header
		pPacketBuffer += 8;  // 8 bytes
		packetSize -= 8;
	
// RecordEventLogger( 'isoc', 'cnt ', pGlobalData->lastSequenceCount, currentSequenceCount);

		if( vSyncDetected ) 
		{
			if( pGlobalData->packetCount == packetPerFrame )		// if we got our frameSync at the right time
			{
				IDHDVCompleteEvent event;

				// post a DV event to let the curious know...
 				event.eventHeader.deviceID	= pGlobalData->deviceID;
 				event.eventHeader.event 	= kIDHPrivateEventReadComplete;
 				event.frameBuffer			= (Ptr) pGlobalData->pImageBuffer;
 				event.bufferSize			= pGlobalData->packetCount * kDVPayloadPacketSize;
				event.fwCycleTime			= (packetHeader & kFWIsochSy) >> kFWIsochSyPhase;
 	
				postEvent(
						pGlobalData->isochComponentGlobals,
						pGlobalData->deviceID,
						kIDHPrivateEventReadComplete,
						(void*)&event);
				
				getNextEmptyInputFrame( pGlobalData->pDVFrameInputData, (UInt32 **) &(pGlobalData->pImageBuffer) ); 	// get a new frame			
			}
	
			pGlobalData->packetCount = 0;							// start a new frame	

			if ((packetSize == kDVPayloadPacketSize) && (currentSequenceCount == pGlobalData->lastSequenceCount))
			{
				// store the packet
				BlockMoveData
				(
					pPacketBuffer,
					(Ptr)((UInt32) pGlobalData->pImageBuffer + (pGlobalData->packetCount * kDVPayloadPacketSize)),
					packetSize
				);
							
				pGlobalData->packetCount++;
			}
		}
		else
		{
			if (currentSequenceCount == pGlobalData->lastSequenceCount)
			{
				if ((pGlobalData->packetCount < packetPerFrame) && (packetSize == kDVPayloadPacketSize))
				{
					// store the packet
					BlockMoveData
					(
						pPacketBuffer,
						(Ptr)((UInt32) pGlobalData->pImageBuffer + (pGlobalData->packetCount * kDVPayloadPacketSize)),
						packetSize
					);
								
					pGlobalData->packetCount++;
				}
				else if ((pGlobalData->packetCount >= packetPerFrame) && (packetSize > 0))
				{
					// too many packets between vSync detection, start new frame
					pGlobalData->packetCount = 0;
					
					RecordEventLogger( 'isoc', 'stor', 0, '2man');
					
				}
			}
			else
			{
				// packet out of sequence, start new frame
				pGlobalData->packetCount = 0;
					
				RecordEventLogger( '****', 'seq!', currentSequenceCount, pGlobalData->lastSequenceCount);
			}
		}

		// if null packet, decrement saved count since DBC is not incremented
		// otherwise set last count to current count to resynch counts if bad sequence
		if (packetSize == 0)	// already deceremented CIP header from packet size
			pGlobalData->lastSequenceCount--;
		else if( packetSize == kDVPayloadPacketSize)
			pGlobalData->lastSequenceCount = currentSequenceCount;
		else
		{
			pGlobalData->lastSequenceCount--;
			RecordEventLogger( '****', 'wird', pGlobalData->lastSequenceCount, packetSize);
//			DebugStr( "\pWeird packet");
		}
		
		// update for next packet
		pCurrentCmd = pCurrentCmd->pNextDCLCommand;
	}
}


///////////////////////////////////////////////////////////////////////
//
// DVDisposeDCLInput
//
///////////////////////////////////////////////////////////////////////

OSErr disposeDCLInput( DVGlobalInPtr pInputData )
{
	Ptr							pDCLCommand;
	DCLCallProcPtr				pDCLPingPongProc;
	short						bufferNum;
	OSErr						error = noErr;
	
	if( pInputData != nil )
	{
		// deallocate receive buffer
		if (pInputData->pReceiveBuffers != nil)
			PoolDeallocate(pInputData->pReceiveBuffers);
		
		// traverse through the DCL program to get to where 
		// the buffers are and deallocate them
		if( pInputData->pDCLList != nil )
		{
			pDCLCommand = (Ptr) pInputData->pDCLList + sizeof (DCLLabel);

			// for all call procs
			for (bufferNum = 0; bufferNum < kNumInputBuffers; bufferNum++)
			{	
				// find the call proc
				pDCLCommand += sizeof (DCLTransferPacket) * kNumPacketsPerInputBuffer +
						sizeof (DCLUpdateDCLList);
		
				// dealloc the call proc
				pDCLPingPongProc = (DCLCallProcPtr) pDCLCommand;
				pDCLCommand += sizeof (DCLCallProc);
				if( pDCLPingPongProc->procData != nil )
					PoolDeallocate( (LogicalAddress) pDCLPingPongProc->procData );
			}
			
			PoolDeallocate( pInputData->pDCLList );
		}
		
		// release update list	
		if( pInputData->ppUpdateDCLList != nil )
			error = PoolDeallocate( pInputData->ppUpdateDCLList );
		
		// release frame IO	
		if( pInputData->pDVFrameInputData != nil )
			error = releaseDVFrameIO( pInputData->pDVFrameInputData );
		
		// dispose the program	
		error = FWDisposeDCLProgram( pInputData->DCLProgramID );
		
		// and the whole bloody structure
		PoolDeallocate( pInputData );
	}
	return( error );
}

OSErr writeFrame(IsochComponentInstancePtr ih, char *tmpBuffPtr)
{
	DeviceDescriptionPtr		pDeviceDescription;
	OSErr						error = noErr;
	
	error = findDeviceDescriptionforDevice(ih, ih->deviceID, &pDeviceDescription);
	
	if (error == noErr)
		error = queueNextFullOutputFrame(pDeviceDescription->pGlobalDVOutData->pDVFrameOutputData, (UInt32 *) tmpBuffPtr);
	FailWithVal( error != noErr, Exit, error);	
	
Exit:		
	return error;	
}

OSErr getEmptyOutputFrame(IsochComponentInstancePtr ih, Ptr *tmpBuffPtr, UInt32 *size)
{
	DeviceDescriptionPtr		pDeviceDescription;
	OSErr						error = noErr;
	
	error = findDeviceDescriptionforDevice(ih, ih->deviceID, &pDeviceDescription);
	FailWithVal( error != noErr, Exit, error);
	
	*size = (pDeviceDescription->standard == ntscIn)?kNTSCCompressedBufferSize:kPALCompressedBufferSize;
	
	error = getNextEmptyOutputFrame(pDeviceDescription->pGlobalDVOutData->pDVFrameOutputData, (UInt32 **) tmpBuffPtr);
	FailWithVal( error != noErr, Exit, error);
		
Exit:		
	return error;	
}

//====================================================================================
//
// registerDevice()
//	Register the device and install the FireWire port callback routines and bus reset proc.
//
//====================================================================================
OSStatus registerDevice(
		RegEntryID* inRegEntryID,
		FWDriverID* outDriverID,
		DeviceDescriptionPtr pDeviceDescription)
{
	CSRROMEntryID csrROMEntryID;
	OSStatus err = noErr;

	err = FWRegisterDriver(
			inRegEntryID,
			outDriverID,
			&csrROMEntryID,
			(UInt32) pDeviceDescription);
			
	if (err)
		goto Exit;	
			
	err = FWSetFWClientInitIsochPortProc(*outDriverID, initIsochPort);
	if (err)
		goto Exit;	
	
	err = FWSetFWClientStartIsochPortProc(*outDriverID, startIsochPort);
	if (err)
		goto Exit;	
	
	err = FWSetFWClientStopIsochPortProc(*outDriverID, stopIsochPort);
	if (err)
		goto Exit;	

	err = FWSetFWClientReleaseIsochPortProc(*outDriverID, releaseIsochPort);

Exit:		
	return err;
}

// handle any reset notifications for the device
OSStatus handleBusReset(FWClientInterfaceParamsPtr pParams, UInt32 *pCommandAcceptance)
{
	DeviceDescriptionPtr pDeviceDescription;
	OSErr error = noErr;
	
	pDeviceDescription = (DeviceDescriptionPtr) pParams->fwClientSpecificData;
	
	RecordEventLogger( 'bus ', 'res ', (UInt32) pDeviceDescription->fwClientID, 0);
	
	FWClientCommandIsComplete(pParams->fwClientCommandID, error);
	*pCommandAcceptance = kFWClientCommandAcceptNoMore;
	
	error = postEvent( pDeviceDescription->componentGlobals, pDeviceDescription->deviceID, kIDHEventDeviceChanged, nil);
	
	return error;
}