/*
	File:		DVFamilyLib.c

	Contains:	This is the client API for talking to DV FireWire devices.

	Version:	xxx put version here xxx

	Written by:	Steve Smith

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

	File Ownership:

		DRI:				xxx put dri here xxx

		Other Contact:		xxx put other contact here xxx

		Technology:			xxx put technology here xxx

	Writers:

		(KW)	Kevin Williams
		(jkl)	Jay Lloyd
		(RS)	Richard Sepulveda
		(CP)	Collin Pieper
		(CLP)	Collin Pieper
		(AW)	Adrienne Wang
		(SS)	Steve Smith

	Change History (most recent first):

		<30>	 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.
		<29>	  1/4/99	GDW		Changed DVFamily names.
		<28>	 12/9/98	SS		DVGetDeviceInfo() now fills in the FireWire ID for the local
									node in the DVDeviceInfo struct. DVGetDeviceClock now returns a
									component instead of a component instance.
		<27>	11/27/98	SS		Changed DVIdle() to process all task-level events that are
									queued, rather than just one.
		<26>	11/23/98	SS		DVCancelNotification() now removes any occurrance of the given
									notification from the list of DV events that were queued for
									task time.
		<25>	11/20/98	SS		Moved DVPostEvents() call to the bottom of all functions that
									call it.
		<24>	11/19/98	SS		Added DVPostEvent() calls to Enable/Disable functions. Changed
									PostEventSIH to queue certain events that need to be reported at
									task level (see comments in PostEventSIH()). Added DVIdle()
									call, but currently only calling it from a Notification Mgr
									proc.
		<23>	11/16/98	SS		Beefed up DVGetDeviceInfo(). Notification now uses PB queues.
									NotifyMeWhen now sets deviceID based on passed connectionID.
									Changed DVPostEvent() to always run at secondary interrupt level
									to solve some reentry problems.
		<22>	11/13/98	SS		Added DVGetDeviceClock() implementation.
		<21>	 11/6/98	KW		in DVGetDeviceInfo, return if device is read enabled or write
									enabled. Allows clients to determine if devices are availiable
									for video in
		<20>	10/30/98	SS		More updates and bug fixes for hot swapping.
		<19>	10/29/98	SS		Was using local ptr before it was initialized in
									DVOpenDeviceConnection(). Doh!
		<18>	10/28/98	SS		Changed open/close connection routines to reinstate
									numConnections. Changed enable/disable read/write routines to
									better police when they can occur, and to enable multiple
									readers. These changes help address hot plugging, multiple
									client and multiple device issues.
		<17>	10/21/98	SS		Changed DVEnableRead() and DVDisableRead() to enable multiple
									readers, i.e. both vdig and sound input driver.
		<16>	 9/17/98	SS		Restructured file & added Sean-like pragmas to make it easier to
									find things. Added generic dv event notification support. Added
									dummy Enable/Disable AVC transaction routines. Removed
									AppleEvent support.
		<15>	 9/10/98	SS		Somewhat gratuitous semantic change in the DV API to further
									abstract the implementation, specifically, from DVDeviceRefNum
									to DVDeviceConnectionID, and from DVOpen/CloseDriver to
									DVOpen/CloseDeviceConnection.
		<14>	  9/3/98	SS		Checked in first pass of dv device info stuff.
		<13>	 3/19/98	RS		Remove DVNames usage due to strange crashes and its non-usage in
									the current version of Oxcart.
		<12>	 3/16/98	SS		Oops. I put the refNum stuff in the wrong place in DVOpenDriver.
									I moved it and un-did Jay's changes.
		<11>	 3/15/98	jkl		Commented out the return noErr line at the start of
									DVOpenDriver. The exporter was failing when it called this and
									expected to get a valid refNum.
		<10>	 3/12/98	SS		Changed implementation of DV driver API to use deviceIDs and
									refNums instead of driverIDs everywhere. DVDriverIDs are still
									used internally for now.
		 <9>	  3/8/98	RS		Added DVIsEnabled() call to library. It returns whether
									specified device is enabled. CP also eliminated OpenDriver and
									CloseDriver function from library.
		 <8>	  2/6/98	SS		Backing out the disconnection checks for a6 build.
		 <7>	  2/4/98	CP		Change DVGetDevicesStandard to return our own standard types and
									return an error for unknown standards...
		 <6>	 1/26/98	CP		Added more checks for disconnection errors...
		 <5>	 1/21/98	SS		Changed AVC stuff, so need to add #include "AVCSupport.h".
		 <4>	 1/19/98	CP		Added driver ID validation code and fixed a little bug in
									DVDriverClose...
		 <3>	 1/14/98	GDW		New interfaces.
		 <2>	 1/12/98	SS		Checked in Collin's changes: added new API implementation,
									removed multitude of specific AVC calls. Some functions formerly
									in this file are now in DVFamilyInternal.c.
		 <1>	 8/18/97	CLP		first checked in
		 <9>	 3/27/97	SS		Forgot to take out debug breaks.
		 <8>	 3/27/97	SS		Moved global allocation/deallocation from the expert to
									init/term routines. Added function to determine whether lib is
									fully initialized.
		 <7>	  3/5/97	AW		minor change
		 <6>	  3/4/97	AW		Changed AVCEnableDVCGrab so that it checks if device is NTSC/PAL
									and allocates mem appropriately
		 <5>	  3/3/97	AW		Fixed wrong command length in GetMediumInfo; added AVC commands
									for setting/getting signals
		 <4>	 2/19/97	SS		Updated to 1.0a2 FSL
		 <3>	 2/12/97	AW		Updated to 1.0d18 FSL
		 <2>	10/31/96	SS		Added helper routines for device control.

*/

#include <Notification.h>

#include "DVFamilyPriv.h"
#include "DVFamilyInternal.h"
#include "Processes.h"
#include "AVCSupport.h"


///////////////////////////////////////////////////////////////////////////
//
// globals
//
///////////////////////////////////////////////////////////////////////////

DVFamilyDataPtr		gpFamilyGlobals = nil;

///////////////////////////////////////////////////////////////////////////
//
// internal prototypes
//
///////////////////////////////////////////////////////////////////////////

OSStatus		PostEventSIH( void* p1, void* p2 );
OSErr			DVIdle( void );
void 			myNMHandler( NMRecPtr pNM );

#pragma mark ееееееееее Public Interface Calls ееееееееее

////////////////////////////////////////////////////////////////////////////////
// These are the application level interfaces routines for the DVFamily
////////////////////////////////////////////////////////////////////////////////

#pragma mark -
#pragma mark ееееееееее Device Management ееееееееее

//////////////////////////////////////////////
//
// DVCountDevices
//
//   This routine counts the number of attatched DV devices
//

UInt32 DVCCountDevices( void )
{
	return( gpFamilyGlobals->numDVDrivers );
}

//////////////////////////////////////////////
//
// DVGetIndDevice
//
//   Given an index in the range of 1 to the number of devices returned by 
// DVCountDevices, DVGetIndDevice returns a deviceID.  If you call DVGetIndDevice
// repeatedly over the entire range of the index, it returns unique device IDs for
// all currently connected and active DV devices.
//
//zzz maybe some speed optimizations for repetative calls would be nice
//    but how many DV devices are going to be connected to a machine anyway...

OSErr DVCGetIndDevice( DVCDeviceID * pDVDevice, UInt32 index )
{
	DVDriverDataPtr				pDVDriverData;
	UInt32						i;
	UInt32						count;
	OSErr						error = noErr;

	if( (index <= gpFamilyGlobals->numDVDrivers) && (index > 0 ) )
	{
		count = gpFamilyGlobals->numDVDrivers - index + 1; // cause devices are inserted at the head
		pDVDriverData = gpFamilyGlobals->pDVDriverList;
		for( i = 1; i < count; i++ )	
		{
			pDVDriverData = pDVDriverData->pNextDVDriverData;
		}
		*pDVDevice = (DVCDeviceID) pDVDriverData;
	}
	else
	{
		*pDVDevice = kInvalidDVDeviceID;
		error = paramErr;
	}
	
	return( error );
}

//////////////////////////////////////////////
//
// DVGetDeviceInfo
//
//   This routine returns DV device info
//

OSErr DVCGetDeviceInfo( DVCDeviceID deviceID, DVCDeviceInfoPtr pInfo )
{
	OSErr				error = noErr;
	DVDriverDataPtr		pDriverData;
	register char		*pSrc, *pDest;
	int					i;
	
	// make sure we've got a valid driverID
	// (actually, this could be a connID or a deviceID)
	error = DVIsValidID( (DVDriverID) deviceID );
	if( error )
		return( error );

	// for now, the deviceID _is_ the ptr to data
	pDriverData = (DVDriverDataPtr) deviceID;
	
	pInfo->dvDeviceID 				= pDriverData->dvDriverID;
	pInfo->uniqueID 				= pDriverData->uniqueID;
	pInfo->vendorID 				= nil;								// for now...
	pInfo->regEntryID 				= pDriverData->deviceRegistryID;

	pInfo->deviceIsOnline 			= !pDriverData->deviceDisconnected;

	pInfo->AVCisEnabled 			= pDriverData->AVCEnabled;

	pInfo->readChannel.isEnabled 	= pDriverData->readEnabled;
	pInfo->writeChannel.isEnabled 	= pDriverData->writeEnabled;

	// copy name str
	pDest = (char*) &(pInfo->deviceName);
	pSrc = (char*) &(pDriverData->name);
	for( i = 0; i <= 255; i++ )
		pDest[i] = pSrc[i];
	
	// remember our local node id
	pInfo->localNodeID 				= pDriverData->localID;
	pInfo->fwClientID 				= pDriverData->fwClientID;

	return( error );
}

//////////////////////////////////////////////
//
// DVGetDeviceName
//
//   This routine returns the name of a specific DV device
//

OSErr DVCGetDeviceName( DVCDeviceID deviceID, char * str )
{
	DVDriverDataPtr				pDVDriverData;
	OSErr						error = noErr;
	short						i;
	
	// make sure we've got a valid deviceID
	error = DVIsValidID( (DVDriverID) deviceID );
	if( error )
		return( error );
		
	// extract our driver data pointer from our supposedly opaque reference
	pDVDriverData = (DVDriverDataPtr) deviceID;
	
	// copy name str
	for( i = 0; i <= 255; i++ )
		str[i] = pDVDriverData->name[i];
		
	return( error );
}

//////////////////////////////////////////////
//
// DVSetDeviceName
//
//   This routine sets the name of a specific DV device
//

OSErr DVCSetDeviceName( DVCDeviceID deviceID, char * str )
{
	DVDriverDataPtr				pDVDriverData;
	OSErr						error = noErr;
	short						i;
	
	// make sure we've got a valid deviceID
	error = DVIsValidID( (DVDriverID) deviceID );
	if( error )
		return( error );
		
	// extract our driver data pointer from our supposedly opaque reference
	pDVDriverData = (DVDriverDataPtr) deviceID;
	
	// copy name str
	for( i = 0; i <= 255; i++ )
		pDVDriverData->name[i] = str[i];
	
//еее We aren't going to support DVNames in this version of Oxcart
//	// add name to prefs file (if add fails, don't pass error code back up to client)
//	DVAddName( &(pDVDriverData->uniqueID), pDVDriverData->name );
	
	return( error );
}

//////////////////////////////////////////////
//
// DVOpenDeviceConnection
//
//   This routine opens a connection to a particular driver
//

OSErr DVCOpenDeviceConnection( DVCDeviceID deviceID, DVCDeviceConnectionID *pConnID )
{
	DVDriverDataPtr		pDVDriverData;
	OSErr				error = noErr;

	// make sure we've got a valid deviceID
	error = DVIsValidID( (DVDriverID) deviceID );
	if( error )
		return( error );

	// extract our driver data pointer from our supposedly opaque reference
	pDVDriverData = (DVDriverDataPtr) deviceID;
	
	// don't bother continuing if we're disconnected
	if( pDVDriverData->deviceDisconnected )
		return( kDVDisconnectedErr );
		
	// add a connection
	pDVDriverData->numConnections++;
	
	// this is redundant, but we'll do it so we can possibly enhance
	// multiple client support sometime in the future.
	*pConnID = (DVCDeviceConnectionID) deviceID;
	
	//zzz could actually open driver here, but advantage would probably be minimal...
	
	return( error );
}

//////////////////////////////////////////////
//
// DVCloseDeviceConnection
//
//   This routine opens a connection to a particular driver
//

OSErr DVCCloseDeviceConnection( DVCDeviceConnectionID connID )
{
	DVDriverDataPtr		pDVDriverData;
	OSErr				error = noErr;
	
	// make sure we've got a valid deviceID
	error = DVIsValidID( (DVDriverID) connID );
	if( error )
		return( error );
		
	// extract our driver data pointer from our supposedly opaque reference
	pDVDriverData = (DVDriverDataPtr) connID;
	
	// remove a connection
	pDVDriverData->numConnections--;
	
	//zzz for apple events managing driver disposal here would be good
	if( ( pDVDriverData->numConnections == 0 ) && (  pDVDriverData->deviceDisconnected ) )
		DVDisposeDriver( (DVDriverID) connID );
		
	return( error );
}

//////////////////////////////////////////////
//
// DVGetDeviceClock
//
//   This routine opens a connection to a particular driver
//

OSErr DVCGetDeviceClock( DVCDeviceID deviceID, Component *clock )
{
	*clock = ((DVDriverDataPtr)deviceID)->clock;
	
	return( noErr );
}


#pragma mark -
#pragma mark ееееееееее DV Event Notification Calls ееееееееее

///////////////////////////////////////////////////////////////////////
//
// DVNewNotification
//
//	create new notification record
//
OSErr DVCNewNotification( DVCDeviceConnectionID connID, DVCNotifyProc notifyProc,
						void *userData, DVCNotificationID *pNotifyID )
{
	DVNotificationEntryPtr	pEntry;
	DVCDeviceID				deviceID;
	OSErr					error = noErr;

	// check the parameters
	if ( notifyProc == nil )
		error = paramErr;
	
	// create new entry
	if ( error == noErr )
	{
		pEntry = (DVNotificationEntryPtr) 
				PoolAllocateResident( sizeof( DVNotificationEntry ), true );
		if ( pEntry == nil )
			error = memFullErr;
	}
	
	// get the deviceID from the connectionID
	deviceID = (DVCDeviceID) connID;
	
	// fill it out
	if ( error == noErr )
	{
		pEntry->deviceID						= deviceID;
		pEntry->wantedEvents					= nil;
		pEntry->notifyProc						= notifyProc;
		pEntry->userRefCon						= userData;
	
		*pNotifyID = (DVCNotificationID) pEntry;	// notification id

		// put new entry at the back of the line
		error = PBEnqueueLast( (QElemPtr) pEntry, gpFamilyGlobals->notificationQueue );
	}
	
	return error;	
}
	
//////////////////////////////////////////////
//
// DVNotifyMeWhen
//
//	activate notification 
//
OSErr DVCNotifyMeWhen( DVCDeviceConnectionID connID, DVCNotificationID notifyID, UInt32 events)
{
	DVNotificationEntryPtr	pEntry;
	DVCDeviceID				deviceID;
	OSErr					error = noErr;
	
	// check the parameters
	if ( events & kDVEveryEvent == nil )
			error = paramErr;
	
	// get the deviceID from the connectionID
	deviceID = (DVCDeviceID) connID;
	
	if ( error == noErr )
	{
		pEntry = (DVNotificationEntryPtr) notifyID;
		
		if ( pEntry != nil )
		{
			pEntry->wantedEvents = events;
			// this is sort of a back door - you can specify any device here
			pEntry->deviceID = deviceID;
		}
		else
			error = paramErr;
	}
		
	return error;	
}

//////////////////////////////////////////////
//
// DVCancelNotification
//
//	deactivate notification
//
OSErr DVCCancelNotification( DVCDeviceConnectionID connID, DVCNotificationID notifyID )
{
	DVNotificationEntryPtr	pEntry;
	OSErr					error = noErr;
	DVEventEntryPtr			pEventEntry;
	
	pEntry = (DVNotificationEntryPtr) notifyID;
	
	if ( pEntry != nil )
	{
		// don't notify this guy
		pEntry->wantedEvents = 0L;
		
		// check the queue to make sure he's not about to be notified.
		pEventEntry = (DVEventEntryPtr) gpFamilyGlobals->receivedDVEvents->qHead;
		while ( pEventEntry )
		{
			if ( pEventEntry->eventRec.eventHeader.notifID == notifyID )
				PBDequeue( (QElemPtr) pEventEntry, gpFamilyGlobals->receivedDVEvents );
				// could be in the queue more than once, so keep going...
				
			pEventEntry = (DVEventEntryPtr) pEventEntry->qLink;
		}
	}
	else
		error = paramErr;
		
	return error;	
}

//////////////////////////////////////////////
//
// DVDisposeNotification
//
//	remove notification from list
//
OSErr DVCDisposeNotification( DVCDeviceConnectionID connID, DVCNotificationID notifyID )
{
	DVNotificationEntryPtr	pEntry;
	OSErr					error = noErr;
	
	// we're not going to do a check, but it's REAL important not to call
	// this function from anywhere but task level.

	// go find the entry and remove it from the list
	pEntry = (DVNotificationEntryPtr) notifyID;
	
	if ( pEntry != nil )
	{
		PBDequeue( (QElemPtr) pEntry, gpFamilyGlobals->notificationQueue );
		PoolDeallocate( (void*) pEntry );
	}
	else
		error = paramErr;
	
	return error;	
}


#pragma mark -
#pragma mark ееееееееее DV Isoch Read Calls ееееееееее

//////////////////////////////////////////////
//
// DVEnableRead
//
//   This routine initializes the driver for input
//

OSErr DVCEnableRead( DVCDeviceConnectionID connID )
{
	DVBasicCmdParams			intParams;
	OSErr						error = noErr;
	DVDriverDataPtr				pDVDriverData;
	DVCEventRecord				theEvent;
		
	// make sure we've got a valid driverID
	error = DVIsValidID( (DVDriverID) connID );
	if( error )
		return( error );

	// extract our driver data pointer from our supposedly opaque reference
	pDVDriverData = (DVDriverDataPtr) connID;
	
	// make sure we're not already write enabled
	if ( pDVDriverData->writeEnabled )
		return ( kAlreadyEnabledErr );
		
	// add a connection
	pDVDriverData->numReaders++;
	
	// if this is the first connection, call the driver
	// to set the read up.
	if ( pDVDriverData->numReaders == 1 )
	{
		// set up driver's enable isoch read parameters
		intParams.interfaceSelector = kDVCEnableIsochRead;
	
		error = DVCallDriver( (DVDriverID) connID, (Ptr) &intParams );
		
		if ( error == noErr )
			pDVDriverData->readEnabled = true;
	}

	if ( error == noErr )
	{
		// post a DV event to let the curious know...
		theEvent.eventHeader.deviceID	= (DVCDeviceID) connID;
		theEvent.eventHeader.theEvent 	= kDVIsochReadEnabled;
		DVCPostEvent( &theEvent );
	}

	return( error );
}

//////////////////////////////////////////////
//
// DVDisableRead
//
//   This routine deinitializes the driver for input
//

OSErr DVCDisableRead( DVCDeviceConnectionID connID )
{
	DVBasicCmdParams			intParams;
	OSErr						error = noErr;
	DVDriverDataPtr				pDVDriverData;
	DVCEventRecord				theEvent;

	// make sure we've got a valid driverID
	error = DVIsValidID( (DVDriverID) connID );
	if( error )
		return( error );

	// extract our driver data pointer from our supposedly opaque reference
	pDVDriverData = (DVDriverDataPtr) connID;
	
	// delete a connection
	pDVDriverData->numReaders--;
	
	// if this is the last connection, call the driver
	// to tear down the read.
	if ( pDVDriverData->numReaders == 0 )
	{
		// set up driver's disable isoch read parameters
		intParams.interfaceSelector = kDVCDisableIsochRead;
	
		error = DVCallDriver( (DVDriverID) connID, (Ptr) &intParams );
		
		// even if there's an error, we're done
		pDVDriverData->readEnabled = false;
	}
	
	if ( error == noErr )
	{
		// post a DV event to let the curious know...
		theEvent.eventHeader.deviceID	= (DVCDeviceID) connID;
		theEvent.eventHeader.theEvent 	= kDVIsochReadDisabled;
		DVCPostEvent( &theEvent );	
	}

	return( error );
}

//////////////////////////////////////////////
//
// DVReadFrame
//
//   This routine reads a frame of data from the DV device
//

OSErr DVCReadFrame( DVCDeviceConnectionID connID, Ptr *ppReadBuffer, UInt32 * pSize )
{
	DVGetBufferParams			intParams;
	OSErr						error = noErr;
	
	// set up driver's enable isoch read parameters
	intParams.interfaceSelector = kDVCReadIsochData;
	intParams.ppBuffer = ppReadBuffer;
	intParams.pBufferSize = pSize;
	
	error = DVCallDriver( (DVDriverID) connID, (Ptr) &intParams );	

	return( error );
}

//////////////////////////////////////////////
//
// DVReleaseFrame
//
//   This routine returns frame of data to the driver for use
//

OSErr DVCReleaseFrame( DVCDeviceConnectionID connID, Ptr pReadBuffer )
{
	DVPassBufferParams			intParams;
	OSErr						error = noErr;

	// set up driver's enable isoch read parameters
	intParams.interfaceSelector = kDVCReleaseReadBuffer;
	intParams.pBuffer = pReadBuffer;
	
	error = DVCallDriver( (DVDriverID) connID, (Ptr) &intParams );	

	return( error );
}

#pragma mark -
#pragma mark ееееееееее DV Isoch Write Calls ееееееееее

//////////////////////////////////////////////
//
// DVEnableWrite
//
//   This routine initializes the driver for output
//

OSErr DVCEnableWrite( DVCDeviceConnectionID connID )
{
	DVBasicCmdParams			intParams;
	DVDriverDataPtr				pDVDriverData;
	OSErr						error = noErr;
	DVCEventRecord				theEvent;

	// extract our driver data pointer from our supposedly opaque reference
	pDVDriverData = (DVDriverDataPtr) connID;
	
	// make sure we're not already enabled for reading or writing
	if ( pDVDriverData->readEnabled || pDVDriverData->writeEnabled )
		return ( kAlreadyEnabledErr );
		
	// set up driver's enable isoch read parameters
	intParams.interfaceSelector = kDVCEnableIsochWrite;

	error = DVCallDriver( (DVDriverID) connID, (Ptr) &intParams );
	
	if ( error == noErr )
	{
		pDVDriverData->writeEnabled = true;
		
		// post a DV event to let the curious know...
		theEvent.eventHeader.deviceID	= (DVCDeviceID) connID;
		theEvent.eventHeader.theEvent 	= kDVIsochWriteEnabled;
		DVCPostEvent( &theEvent );
	}
		
	return( error );

}

//////////////////////////////////////////////
//
// DVDisableWrite
//
//   This routine deinitializes the driver for output
//

OSErr DVCDisableWrite( DVCDeviceConnectionID connID )
{
	DVBasicCmdParams			intParams;
	DVDriverDataPtr				pDVDriverData;
	OSErr						error = noErr;
	DVCEventRecord				theEvent;

	// extract our driver data pointer from our supposedly opaque reference
	pDVDriverData = (DVDriverDataPtr) connID;
	
	// set up driver's enable isoch read parameters
	intParams.interfaceSelector = kDVCDisableIsochWrite;

	error = DVCallDriver( (DVDriverID) connID, (Ptr) &intParams );	
	
	// even if there's an error, we're done
	pDVDriverData->writeEnabled = false;
	
	// post a DV event to let the curious know...
	theEvent.eventHeader.deviceID	= (DVCDeviceID) connID;
	theEvent.eventHeader.theEvent 	= kDVIsochWriteDisabled;
	DVCPostEvent( &theEvent );

	return( error );

}

//////////////////////////////////////////////
//
// DVGetEmptyFrame
//
//   This routine retrieves an empty frame from the driver for output
//

OSErr DVCGetEmptyFrame( DVCDeviceConnectionID connID, Ptr *ppEmptyFrameBuffer, UInt32 * pSize )
{
	DVGetBufferParams			intParams;
	OSErr						error = noErr;

	// set up driver's enable isoch read parameters
	intParams.interfaceSelector = kDVCGetEmptyFrame;
	intParams.ppBuffer = ppEmptyFrameBuffer;
	intParams.pBufferSize = pSize;
	
	error = DVCallDriver( (DVDriverID) connID, (Ptr) &intParams );	
	return( error );
}

//////////////////////////////////////////////
//
// DVWriteFrame
//
//   This routine sends a frame of data to the camera
//

OSErr DVCWriteFrame( DVCDeviceConnectionID connID, Ptr pWriteBuffer )
{
	DVPassBufferParams			intParams;
	OSErr						error = noErr;

	// set up driver's enable isoch read parameters
	intParams.interfaceSelector = kDVCWriteFrame;
	intParams.pBuffer = pWriteBuffer;
	
	error = DVCallDriver( (DVDriverID) connID, (Ptr) &intParams );	
	return( error );
}

#pragma mark -
#pragma mark ееееееееее AVC Transaction Calls ееееееееее

//////////////////////////////////////////////
//
// DVEnableAVCTransactions
//
//   This routine initializes the driver for 
//	 performing avc transactions
//
OSErr DVCEnableAVCTransactions( DVCDeviceConnectionID connID )
{
	OSErr						error = noErr;

	return( error );
}

//////////////////////////////////////////////
//
// DVDoAVCTransaction
//
//   This routine sends a transaction block to the driver
//

OSErr DVCDoAVCTransaction( DVCDeviceConnectionID connID, AVCTransactionParamsPtr pParams )
{
	DVAVCTransactionParams		transactionParams;
	OSErr						error = noErr;
	
	// fill out the internal tansaction block
	transactionParams.interfaceSelector 	= kAVCDoTransaction;
	transactionParams.commandBufferPtr		= pParams->commandBufferPtr;
	transactionParams.commandLength			= pParams->commandLength;
	transactionParams.responseBufferPtr		= pParams->responseBufferPtr;
	transactionParams.responseBufferSize	= pParams->responseBufferSize;
	transactionParams.responseHandler		= pParams->responseHandler;

	error = DVCallDriver( (DVDriverID) connID, (Ptr) &transactionParams );

	return( error );
}

//////////////////////////////////////////////
//
// DVDisableAVCTransactions
//
//   This routine deinitializes the driver for 
//	 performing avc transactions
//
OSErr DVCDisableAVCTransactions( DVCDeviceConnectionID connID )
{
	OSErr						error = noErr;

	return( error );
}

#pragma mark -
#pragma mark ееееееееее To be discontinued... ееееееееее

//////////////////////////////////////////////
//
// DVIsEnabled
//
//   This routine tells if this device is enabled
//

OSErr DVCIsEnabled( DVCDeviceConnectionID connID, Boolean *isEnabled)
{
	DVDriverDataPtr		pDVDriverData;
	OSErr				error = noErr;
	
	// make sure we've got a valid driverID
	error = DVIsValidID( (DVDriverID) connID );
	if( error )
		return( error );

	pDVDriverData = (DVDriverDataPtr) connID;
	
	// is it enabled
	if( pDVDriverData->numConnections > 0)
		*isEnabled = true;
	else
		*isEnabled = false;
		
	return error;
}

//////////////////////////////////////////////
//
// DVGetDeviceStandard
//
//   This routine returns the video standard
//

OSErr DVCGetDeviceStandard( DVCDeviceConnectionID connID, UInt32 * pStandard )
{
	AVCCTSFrameStruct		avcFrame;
	AVCTransactionParams	transactionParams;
	UInt8					responseBuffer[ 16 ];
	OSErr					theErr = noErr;
	UInt32					currentSignal, AVCStatus;
	
	// 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;
	
	theErr = DVCDoAVCTransaction( (DVDriverID) connID, &transactionParams );

	currentSignal = ((responseBuffer[ 2 ] << 8) | responseBuffer[ 3 ]);
	AVCStatus = responseBuffer[ 0 ];
	
	*pStandard = kUnknownStandard;
	switch (currentSignal & 0x000000ff) 
	{
		case kAVCSignalModeSD525_60: 
		case kAVCSignalModeSDL525_60:
		case kAVCSignalModeHD1125_60: 
			*pStandard = kNTSCStandard;
			return( theErr );
	
		case kAVCSignalModeSD625_50: 
		case kAVCSignalModeSDL625_50: 
		case kAVCSignalModeHD1250_50: 
			*pStandard = kPALStandard;
			return( theErr );
	
		default:
			return( kUnknownStandardErr ); // how should I handle this?
	}
}

#pragma mark -
#pragma mark ееееееееее Private Interface Calls ееееееееее
#pragma mark -

///////////////////////////////////////////////////////////////////////
//
// DVPostEvent
//
//	used for sending the real notification
//
///////////////////////////////////////////////////////////////////////
OSErr DVCPostEvent( DVCEventRecordPtr	pEvent )
{
	OSErr					error = noErr;

	// make sure it's a legit event
	if ( (pEvent->eventHeader.theEvent & kDVEveryEvent) == nil )
			error = paramErr;
	
	// get to secondary interrupt level as quickly as possible, where
	// things are nice and synchronized...
	error = CallSecondaryInterruptHandler2( (SecondaryInterruptHandler2) PostEventSIH,
											nil,
											pEvent,
											nil );
	
	// now that we've pre-processed the event, give task level
	// notifications a chance to run, if appropriate.
	if ( CurrentExecutionLevel() == kTaskLevel )
		DVIdle();
											
	return( error );
}


///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
OSStatus
PostEventSIH( void* p1, void* p2 )
{
	DVCEventRecordPtr			pEvent = (DVCEventRecordPtr) p1;
	DVEventEntryPtr				pEventEntry;
	DVNotificationEntryPtr		pEntry;
	OSErr						error = noErr;

	// We now have two broad classifications of events - ones that need to be
	// reported ASAP, which are stream related:
	//
	// 		kDVIsochReadComplete
	//		kDVIsochWriteComplete
	//
	// and ones that are device management related, whose notifications will
	// probably generate massive amounts of task-level only Toolbox calls:
	//
	//		kDVDeviceAdded
	//		kDVDeviceRemoved
	//		kDVIsochReadEnabled
	//		kDVIsochReadDisabled
	//		kDVIsochWriteEnabled
	//		kDVIsochWriteDisabled
	//
	// We ship the low-latency notifications to secondary interrupt, while
	// the task level calls we queue and get back to them when someone
	// calls DVIdle().
	//
	
	// ok, so let's go find out who's waiting for this event

	// go through list looking for the curious
	pEntry = (DVNotificationEntryPtr) gpFamilyGlobals->notificationQueue->qHead;
	while ( pEntry != nil )
	{
		if ( (pEvent->eventHeader.theEvent & pEntry->wantedEvents) != nil )
		{
			// only send notification if it's a global connection id or if
			// the event came from the same deviceID as this notif entry
			if ( (pEntry->deviceID == kDVGlobalEventConnectionID) || 
				(pEvent->eventHeader.deviceID == pEntry->deviceID) )
			{				
				// we currently only support a one-shot notification, like clock callbacks
				pEntry->wantedEvents = nil;
				
				// make sure the event contains this notification id
				pEvent->eventHeader.notifID = (DVCNotificationID) pEntry;
				

				// check before calling..
				switch( pEvent->eventHeader.theEvent )
				{
					case kDVIsochReadComplete:
					case kDVIsochWriteComplete:
						// process event immediately...
						error = (*pEntry->notifyProc)( pEvent, pEntry->userRefCon );
						break;
			
					case kDVDeviceAdded:
					case kDVDeviceRemoved:
					case kDVIsochReadEnabled:
					case kDVIsochReadDisabled:
					case kDVIsochWriteEnabled:
					case kDVIsochWriteDisabled:
						// queue the event and proc for later processing...
						
						// get an entry
						error = PBDequeueFirst( gpFamilyGlobals->availableDVEvents,
												(QElemPtr*) &pEventEntry );
						
						// if we don't have any more available event elements, 
						// we just drop the events on the floor
						
						// copy the notify proc & refcon
						if ( error == noErr )
						{
							pEventEntry->notifyProc	= pEntry->notifyProc;
							pEventEntry->userRefCon = pEntry->userRefCon;
						}
						
						// copy the event
						if ( error == noErr )
							BlockCopy( pEvent, &(pEventEntry->eventRec), sizeof( DVCEventRecord ) );
						
						// queue it
						if ( error == noErr )
							PBEnqueue( (QElemPtr) pEventEntry, gpFamilyGlobals->receivedDVEvents );
							
						// If we haven't already sent notification 
						// to Notification Mgr to run tasks, do it now...
						if ( CompareAndSwap( false, true, &(gpFamilyGlobals->nmIsInstalled) ) )
							NMInstall( &(gpFamilyGlobals->dvNMRec) );
						
						break;
				
					default:
						break;
				}
	
			}
		}

		// next entry
		pEntry = (DVNotificationEntryPtr) pEntry->qLink;
	}

	return( error );
}


///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
OSErr
DVIdle( void )
{
	DVEventEntryPtr		pEventEntry;
	OSErr				error = noErr;
	
	// this routine should only get called at task level
	
	// go see if there are any task level notifications that
	// need to be done. we take from the back end since that
	// was the oldest one.
	while( true )
	{
		error = PBDequeueLast( gpFamilyGlobals->receivedDVEvents,
								(QElemPtr*) &pEventEntry );
		
		if ( error == noErr )
		{
			if ( pEventEntry->notifyProc )
			{
				// process it...
				error = (*pEventEntry->notifyProc)( &(pEventEntry->eventRec), pEventEntry->userRefCon );
			}
			
			// and put it back into available queue
			PBEnqueue( (QElemPtr) pEventEntry, gpFamilyGlobals->availableDVEvents );
		}
		else
			// we're out of events (probably)
			break;
	}
	
	return( error );
}


///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
void
myNMHandler( NMRecPtr pNM )
{
	do {
		DVIdle();
	} while( CompareAndSwap( true, false, &(gpFamilyGlobals->nmIsInstalled) ) );
	
	// until next time...
	NMRemove( pNM );
}



