DCFireWireDV.cp   [plain text]


/*
	File:		DCFireWireDV.cp

	Contains:	Device Control component for DV on Firewire. 
												

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

	File Ownership:

		DRI:				Kevin Williams

	Writers:

		(jkl)	Jay Lloyd
		(RS)	Richard Sepulveda
		(GDW)	George D. Wilson Jr.

	Change History (most recent first):

		<13>	  8/6/99	jkl		Got rid of DVFamily.h stuff.
		<12>	  8/3/99	RS		Returned device disconnected error when appropriate.
		<11>	 7/28/99	jkl		Used atomic operation to check commandObjectInUse flag for a
									little more safety.
		<10>	 7/27/99	jkl		Added command object in use flag to DoAVCTransaction to make
									sure it is not reentered.
		 <9>	 7/13/99	jkl		Moved setting asynch command object payload from
									DoAVCTransaction to constructor.
									
		 <8>	 7/12/99	RS		Change'd component instance storage back to application heap
									because close is handled correctly in the isoch component now.
		 <7>	  7/8/99	RS		Allocating instance storage using NewPtrSysClear() instead of
									NewPtr() because this component needs to stay open across app
									launches at times.
		 <6>	  7/5/99	RS		Allocating fCommandObjectID in the constructor and deleting in
									destructor to avoid any non-task allocation problems.
		 <5>	  7/5/99	jkl		Changed to set max payload for command object, not client. Moved
									client max payload to isoch component.
		 <4>	  7/5/99	jkl		Set max payload for FCP command object to 512. The default is 4
									bytes and was causing failures on device control commands larger
									than 4 bytes.
		 <3>	 6/21/99	RS		fDeviceEnable wasn't being initialized in the constructor.
									Caused spurious bug during enable.
		 <2>	 6/18/99	GDW		Changed things.
	   <1>	 6/15/99		KW		Created
*/

// DCFireWireDV headers.
#include "DCFireWireDV.h"
#include "DCFireWireDVVersionAndRezIDs.h"			// Version constants and Rez IDs

// MacOS headers.
#include <FireWire.h>
#include <Gestalt.h>
#include <IsochronousDataHandler.h>

// Standard C++ Library headers.
//#include <cstddef>



//
// -------- DCFireWireDV --------
//


#pragma mark ееееееееее Constructor & Destructor ееееееееее



//
// DCFireWireDV()
//
//	Constructor.
//
DCFireWireDV::DCFireWireDV(ComponentInstance self, Boolean *success)
	: fSelf(self),
	fRegistered(false),
	fTarget(0),
	fClientID(0),
	fDeviceEnable(false)	
{
	*success = true;
	
	OSErr error = FWAllocateFCPCommandObject(&fCommandObjectID);
	if (error)
		*success = false;
	else
	{
		// max packet for s100
		FWSetAsynchCommandMaxPayloadSize(fCommandObjectID, 512);
	}	

}


//
// ~DCFireWireDV()
//
//	Destructor.
//
DCFireWireDV::~DCFireWireDV()
{
	if( fCommandObjectID)
		FWDeallocateFWCommandObject(fCommandObjectID);
}



//====================================================================================
//
// DCFireWireDV operator new()
//	This allocates the memory for the DCFireWireDV object data members.
//	Though the global operator new could probably be used safely, this version will
//	be used so that it is not affected by potential changes in the runtime libraries
//	implementation of the global operator new.
//
//====================================================================================
void* DCFireWireDV::operator new(size_t size)
{
	return (void*) NewPtrClear(size);
}



//====================================================================================
//
// DCFireWireDV operator delete()
//	Dispose of the object storage.  Global operator delete is not being used since
//	global operator new isn't.
//
//====================================================================================
void DCFireWireDV::operator delete(void* ptr)
{
	if (NULL != ptr)
		DisposePtr((Ptr) ptr);
}




#pragma mark -
#pragma mark ееееееееее Default Component Calls ееееееееее

//====================================================================================
//
// Open()
//	The Component Manager issues an 'open' request whenever a client tries to open a
//	connection to the component by calling the Open[Default]Component() function.
//
//	Since each ComponentInstance has a separate DCFireWireDV object instance
//	associated with it, the state of each ComponentInstance can be independently
//	maintained.  Any information that needs to be shared between ComponentInstances
//	will be accessible through the ComponentRefcon.
//
//====================================================================================
pascal ComponentResult DCFireWireDV::Open(
		DCFireWireDV* unused,
		ComponentInstance self)
{
	Boolean success;
	
	// Attempt to construct a DCFireWireDV object.
	DCFireWireDV* dc = new DCFireWireDV(self, &success);
	if (NULL == dc || success == false)
		return memFullErr;						// Unable to make new DCFireWireDV
		
		
	// DCFireWireDV was successfully instantiated.  Set this  ComponentInstance's storage
	// to be a pointer to the DCFireWireDV object.  This will allow subsequent calls
	// easy access to the DCFireWireDV that was created for this ComponentInstance
	// since the pointer will be passed in automatically by the dispatch routine.
	
	SetComponentInstanceStorage(dc->fSelf, reinterpret_cast<char**>(dc));



	// 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 DeviceControl, 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.


	SInt32 refcon = GetComponentRefcon(reinterpret_cast<Component>(dc->fSelf));
	
	if (NULL == refcon)
		return (noErr);				// Haven't been registered, so simply return
	

	// Component had been successfully registered, so this is a true 'Open' request.
	dc->fRegistered = true;
	

	// Make sure that the appropriate version of QuickTime is around.  Only versions
	// 4.0 or greater are supported.
	
	enum { kMinimumQuickTimeVersion	= 0x0400 };

	SInt32 gestaltResult;	

	OSErr osErr = Gestalt(gestaltQuickTimeVersion, &gestaltResult);
	if ((noErr != osErr) || (kMinimumQuickTimeVersion > (gestaltResult >> 16))) 
		return qtParamErr;


	return noErr;	
}



//====================================================================================
//
// Close()
//	The Component Manager issues a 'close' request whenever a client closes its 
//	connection to the component by calling the CloseComponent() function.
//
//	As noted on page 6-21 of "IM More Macintosh Toolbox", the Component Manager will
//	issue a close request even if the open request failed.
//
//====================================================================================
pascal ComponentResult DCFireWireDV::Close(
		DCFireWireDV* dc,
		ComponentInstance self)
{
	
	// Make sure that the DCFireWireDV* is non-NULL.  If will be NULL only if the
	// constructor failed in Open().
	
	if (NULL == dc)
		return noErr;
	
	// See if the last component instance is being closed.  If so, deallocate any
	// shared data (if appropriate) and set the ComponentRefcon to NULL to signify
	// that no shared data is allocated.
	
	SInt32 instanceCount = CountComponentInstances((Component) dc->fSelf);
		
	// Last instance is being closed?
	if (1 == instanceCount)	
	{

		if (!dc->fRegistered)
		{
			// The component has been unregistered, so set the ComponentRefcon to
			// NULL to signify that.
			SetComponentRefcon((Component) dc->fSelf, NULL);
		}
	}
	
	// Since this instance is being closed, it's storage will never be referenced
	// again, so set it to NULL (just being paranoid), and delete the DCFireWireDV
	// object.
	
	SetComponentInstanceStorage(dc->fSelf, reinterpret_cast<char**>(0));
	delete dc;

	return noErr;
}



//====================================================================================
//
// Version()
//	The Component Manager issues a 'version' request when a client calls the 
//	GetComponentVersion() function to retrieve the component's version number.
//
//		<- ComponentResult
//		The version number.  The high-order 16 bits represents the major version
//		(the component specification level), and the low-order 16 bits represent the
//		minor version (implementation level).
//
//====================================================================================
pascal ComponentResult DCFireWireDV::Version(DCFireWireDV* dc)
{
		
	return ((kDCFireWireDVInterfaceVersion << 16) | (kDCFireWireDVCodeVersion));
}



//====================================================================================
//
// Register()
//	The Component Manager issues a 'register' request when the component is registered.
//	This gives the component an opportunity to determine whether it can operate in the
//	current environment.  System resources should not normally be allocated in
//	response to a register request.
//
//	When the Component Manager is attempting to register a component, it will send it
//	the following request sequence:  open, register, close.
//
//		<- ComponentResult
//		If the component should be registered, return 'noErr', otherwise return a
//		qtParamErr.
//
//====================================================================================
pascal ComponentResult DCFireWireDV::Register(DCFireWireDV* dc)
{			
	// Everything necessary for a successful registration has occurred.
	// Therefore, following IDH conventions, set the component refCon to 0xFFFFFFFF
	// to signify that and set fRegistered to 'true'
	
	dc->fRegistered = true;
	SetComponentRefcon((Component) dc->fSelf, (long) 0xFFFFFFFF);

		
	return noErr;
}



//====================================================================================
//
// Target()
//	The Component Manager issues a 'target' request to inform the component that it 
//	has been targeted by another component.  After being targeted, the targeted
//	component should call the component that targeted it whenever it would normally
//	have called itself.
//
//		-> parentComponent	ComponentInstance to pass calls on to.
//
//====================================================================================
pascal ComponentResult DCFireWireDV::Target(
		DCFireWireDV* dc,
		ComponentInstance parentComponent)
{
	dc->fTarget = parentComponent;
	// CallComponentTarget needs to be called does it not?
	return noErr;
}



//====================================================================================
//
// Unregister()
//	The Component Manager issues an 'unregister' request when the component is
//	unregistered.  This gives the component an opportunity to perform any clean up
//	operations, such as resetting the hardware.  An 'unregister' request will be sent
//	to the component even if no clients opened the client.
//
//====================================================================================
pascal ComponentResult DCFireWireDV::Unregister(DCFireWireDV* dc)
{
	
	// Set the fRegistered data member to 'false' so that the subsequent Close() will
	// know to set the ComponentRefcon to NULL to signify that the component is no
	// longer registered.
	
	dc->fRegistered = false;
		
	return noErr;
}



#pragma mark -
#pragma mark ееееееееее Public Device Control Calls  ееееееееее



//====================================================================================
//
// DoAVCTransaction()
//
//	ToDo:
//====================================================================================
pascal ComponentResult DCFireWireDV::DoAVCTransaction(
		DCFireWireDV* dc,
		DVCTransactionParams* inTransaction)
{
	ComponentResult result = noErr;

	if ( dc->fClientID == (FWClientID) kIDHInvalidDeviceID )
		return(kIDHErrInvalidDeviceID);
	
	if ( !dc->fDeviceEnable )
		return(kIDHErrDeviceDisconnected);

	if( dc->fCommandObjectID == nil)
		return kIDHErrInvalidDeviceID;
		
	if (CompareAndSwap(0, 1, &dc->fCommandObjectInUse))
	{
	
		// Set up FCP command params to tell camera to do something.
		result = FWSetFWCommandParams(	dc->fCommandObjectID,						// objectID
										dc->fClientID,								// ref ID
										kFWCommandSyncFlag,							// cmd flags
										nil,										// completion proc
										0);											// completion data							
	
		result = FWSetFCPCommandParams(	dc->fCommandObjectID,						// 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
										nil );										// response handler
										
		// send the FCP command
		result = FWSendFCPCommand(dc->fCommandObjectID);

		dc->fCommandObjectInUse = 0;
	}
	else
		result = kIDHErrDeviceBusy;
	
	return result;
}



#pragma mark -
#pragma mark ееееееееее FWDVCodec Private Dispatch Calls  ееееееееее


//====================================================================================
//
// EnableAVCTransactions()
//
//
//====================================================================================
pascal ComponentResult DCFireWireDV::EnableAVCTransactions(
		DCFireWireDV* dc)
{
	ComponentResult				result = noErr;
	
	if ( dc->fClientID != (FWClientID) kIDHInvalidDeviceID )
		dc->fDeviceEnable = true;
	else
		result = kIDHErrDeviceNotOpened;
	
	return result ;
}

//====================================================================================
//
// DisableAVCTransactions()
//
//
//====================================================================================
pascal ComponentResult DCFireWireDV::DisableAVCTransactions(
		DCFireWireDV* dc)
{
	ComponentResult				result = noErr;
	
	dc->fDeviceEnable = false;
	
	return result ;
}

//====================================================================================
//
// SetDeviceConnectionID()
//
//
//====================================================================================
pascal ComponentResult DCFireWireDV::SetDeviceConnectionID(
		DCFireWireDV* dc, DeviceConnectionID connectionID)
{
	ComponentResult		result = noErr;

	if ( dc->fDeviceEnable )
		result = kIDHErrDeviceInUse;
	else
		dc->fClientID = reinterpret_cast<FWClientID>(connectionID);
	
	return result;
}

//====================================================================================
//
// GetDeviceConnectionID()
//
//
//====================================================================================
pascal ComponentResult DCFireWireDV::GetDeviceConnectionID(
		DCFireWireDV* dc, DeviceConnectionID* connectionID)
{
	ComponentResult				result = noErr;
	
	*connectionID = reinterpret_cast<DeviceConnectionID>(dc->fClientID);
	return result ;
}









#pragma mark -
#pragma mark ееееееееее Static Callback Routines еееееее



#pragma mark -
#pragma mark ееееееееее Utility Routines for DCFireWireDV еееееее




#pragma mark -
#pragma mark ееееееееее Static Utility Routines for DCFireWireDV еееееее