1/*
2 * Copyright (c) 2009 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23/*
24	File:		DVFamilyLib.c
25
26	Contains:	This is the client API for talking to DV FireWire devices.
27
28	Version:	xxx put version here xxx
29
30	Written by:	Steve Smith
31
32	Copyright:	� 1996-1999 by Apple Computer, Inc., all rights reserved.
33
34	File Ownership:
35
36		DRI:				xxx put dri here xxx
37
38		Other Contact:		xxx put other contact here xxx
39
40		Technology:			xxx put technology here xxx
41
42	Writers:
43
44		(KW)	Kevin Williams
45		(jkl)	Jay Lloyd
46		(RS)	Richard Sepulveda
47		(CP)	Collin Pieper
48		(CLP)	Collin Pieper
49		(AW)	Adrienne Wang
50		(SS)	Steve Smith
51
52	Change History (most recent first):
53
54		<30>	 6/17/99	jkl		Added fwClientID to DVGetLocalFWReferenceID call to make the
55									fwClientID part of the device info so IDH can execute
56									getDeviceStandard by itself.
57		<29>	  1/4/99	GDW		Changed DVFamily names.
58		<28>	 12/9/98	SS		DVGetDeviceInfo() now fills in the FireWire ID for the local
59									node in the DVDeviceInfo struct. DVGetDeviceClock now returns a
60									component instead of a component instance.
61		<27>	11/27/98	SS		Changed DVIdle() to process all task-level events that are
62									queued, rather than just one.
63		<26>	11/23/98	SS		DVCancelNotification() now removes any occurrance of the given
64									notification from the list of DV events that were queued for
65									task time.
66		<25>	11/20/98	SS		Moved DVPostEvents() call to the bottom of all functions that
67									call it.
68		<24>	11/19/98	SS		Added DVPostEvent() calls to Enable/Disable functions. Changed
69									PostEventSIH to queue certain events that need to be reported at
70									task level (see comments in PostEventSIH()). Added DVIdle()
71									call, but currently only calling it from a Notification Mgr
72									proc.
73		<23>	11/16/98	SS		Beefed up DVGetDeviceInfo(). Notification now uses PB queues.
74									NotifyMeWhen now sets deviceID based on passed connectionID.
75									Changed DVPostEvent() to always run at secondary interrupt level
76									to solve some reentry problems.
77		<22>	11/13/98	SS		Added DVGetDeviceClock() implementation.
78		<21>	 11/6/98	KW		in DVGetDeviceInfo, return if device is read enabled or write
79									enabled. Allows clients to determine if devices are availiable
80									for video in
81		<20>	10/30/98	SS		More updates and bug fixes for hot swapping.
82		<19>	10/29/98	SS		Was using local ptr before it was initialized in
83									DVOpenDeviceConnection(). Doh!
84		<18>	10/28/98	SS		Changed open/close connection routines to reinstate
85									numConnections. Changed enable/disable read/write routines to
86									better police when they can occur, and to enable multiple
87									readers. These changes help address hot plugging, multiple
88									client and multiple device issues.
89		<17>	10/21/98	SS		Changed DVEnableRead() and DVDisableRead() to enable multiple
90									readers, i.e. both vdig and sound input driver.
91		<16>	 9/17/98	SS		Restructured file & added Sean-like pragmas to make it easier to
92									find things. Added generic dv event notification support. Added
93									dummy Enable/Disable AVC transaction routines. Removed
94									AppleEvent support.
95		<15>	 9/10/98	SS		Somewhat gratuitous semantic change in the DV API to further
96									abstract the implementation, specifically, from DVDeviceRefNum
97									to DVDeviceConnectionID, and from DVOpen/CloseDriver to
98									DVOpen/CloseDeviceConnection.
99		<14>	  9/3/98	SS		Checked in first pass of dv device info stuff.
100		<13>	 3/19/98	RS		Remove DVNames usage due to strange crashes and its non-usage in
101									the current version of Oxcart.
102		<12>	 3/16/98	SS		Oops. I put the refNum stuff in the wrong place in DVOpenDriver.
103									I moved it and un-did Jay's changes.
104		<11>	 3/15/98	jkl		Commented out the return noErr line at the start of
105									DVOpenDriver. The exporter was failing when it called this and
106									expected to get a valid refNum.
107		<10>	 3/12/98	SS		Changed implementation of DV driver API to use deviceIDs and
108									refNums instead of driverIDs everywhere. DVDriverIDs are still
109									used internally for now.
110		 <9>	  3/8/98	RS		Added DVIsEnabled() call to library. It returns whether
111									specified device is enabled. CP also eliminated OpenDriver and
112									CloseDriver function from library.
113		 <8>	  2/6/98	SS		Backing out the disconnection checks for a6 build.
114		 <7>	  2/4/98	CP		Change DVGetDevicesStandard to return our own standard types and
115									return an error for unknown standards...
116		 <6>	 1/26/98	CP		Added more checks for disconnection errors...
117		 <5>	 1/21/98	SS		Changed AVC stuff, so need to add #include "AVCSupport.h".
118		 <4>	 1/19/98	CP		Added driver ID validation code and fixed a little bug in
119									DVDriverClose...
120		 <3>	 1/14/98	GDW		New interfaces.
121		 <2>	 1/12/98	SS		Checked in Collin's changes: added new API implementation,
122									removed multitude of specific AVC calls. Some functions formerly
123									in this file are now in DVFamilyInternal.c.
124		 <1>	 8/18/97	CLP		first checked in
125		 <9>	 3/27/97	SS		Forgot to take out debug breaks.
126		 <8>	 3/27/97	SS		Moved global allocation/deallocation from the expert to
127									init/term routines. Added function to determine whether lib is
128									fully initialized.
129		 <7>	  3/5/97	AW		minor change
130		 <6>	  3/4/97	AW		Changed AVCEnableDVCGrab so that it checks if device is NTSC/PAL
131									and allocates mem appropriately
132		 <5>	  3/3/97	AW		Fixed wrong command length in GetMediumInfo; added AVC commands
133									for setting/getting signals
134		 <4>	 2/19/97	SS		Updated to 1.0a2 FSL
135		 <3>	 2/12/97	AW		Updated to 1.0d18 FSL
136		 <2>	10/31/96	SS		Added helper routines for device control.
137
138*/
139
140#include <Notification.h>
141
142#include "DVFamilyPriv.h"
143#include "DVFamilyInternal.h"
144#include "Processes.h"
145#include "AVCSupport.h"
146
147
148///////////////////////////////////////////////////////////////////////////
149//
150// globals
151//
152///////////////////////////////////////////////////////////////////////////
153
154DVFamilyDataPtr		gpFamilyGlobals = nil;
155
156///////////////////////////////////////////////////////////////////////////
157//
158// internal prototypes
159//
160///////////////////////////////////////////////////////////////////////////
161
162OSStatus		PostEventSIH( void* p1, void* p2 );
163OSErr			DVIdle( void );
164void 			myNMHandler( NMRecPtr pNM );
165
166#pragma mark ���������� Public Interface Calls ����������
167
168////////////////////////////////////////////////////////////////////////////////
169// These are the application level interfaces routines for the DVFamily
170////////////////////////////////////////////////////////////////////////////////
171
172#pragma mark -
173#pragma mark ���������� Device Management ����������
174
175//////////////////////////////////////////////
176//
177// DVCountDevices
178//
179//   This routine counts the number of attatched DV devices
180//
181
182UInt32 DVCCountDevices( void )
183{
184	return( gpFamilyGlobals->numDVDrivers );
185}
186
187//////////////////////////////////////////////
188//
189// DVGetIndDevice
190//
191//   Given an index in the range of 1 to the number of devices returned by
192// DVCountDevices, DVGetIndDevice returns a deviceID.  If you call DVGetIndDevice
193// repeatedly over the entire range of the index, it returns unique device IDs for
194// all currently connected and active DV devices.
195//
196//zzz maybe some speed optimizations for repetative calls would be nice
197//    but how many DV devices are going to be connected to a machine anyway...
198
199OSErr DVCGetIndDevice( DVCDeviceID * pDVDevice, UInt32 index )
200{
201	DVDriverDataPtr				pDVDriverData;
202	UInt32						i;
203	UInt32						count;
204	OSErr						error = noErr;
205
206	if( (index <= gpFamilyGlobals->numDVDrivers) && (index > 0 ) )
207	{
208		count = gpFamilyGlobals->numDVDrivers - index + 1; // cause devices are inserted at the head
209		pDVDriverData = gpFamilyGlobals->pDVDriverList;
210		for( i = 1; i < count; i++ )
211		{
212			pDVDriverData = pDVDriverData->pNextDVDriverData;
213		}
214		*pDVDevice = (DVCDeviceID) pDVDriverData;
215	}
216	else
217	{
218		*pDVDevice = kInvalidDVDeviceID;
219		error = paramErr;
220	}
221
222	return( error );
223}
224
225//////////////////////////////////////////////
226//
227// DVGetDeviceInfo
228//
229//   This routine returns DV device info
230//
231
232OSErr DVCGetDeviceInfo( DVCDeviceID deviceID, DVCDeviceInfoPtr pInfo )
233{
234	OSErr				error = noErr;
235	DVDriverDataPtr		pDriverData;
236	register char		*pSrc, *pDest;
237	int					i;
238
239	// make sure we've got a valid driverID
240	// (actually, this could be a connID or a deviceID)
241	error = DVIsValidID( (DVDriverID) deviceID );
242	if( error )
243		return( error );
244
245	// for now, the deviceID _is_ the ptr to data
246	pDriverData = (DVDriverDataPtr) deviceID;
247
248	pInfo->dvDeviceID 				= pDriverData->dvDriverID;
249	pInfo->uniqueID 				= pDriverData->uniqueID;
250	pInfo->vendorID 				= nil;								// for now...
251	pInfo->regEntryID 				= pDriverData->deviceRegistryID;
252
253	pInfo->deviceIsOnline 			= !pDriverData->deviceDisconnected;
254
255	pInfo->AVCisEnabled 			= pDriverData->AVCEnabled;
256
257	pInfo->readChannel.isEnabled 	= pDriverData->readEnabled;
258	pInfo->writeChannel.isEnabled 	= pDriverData->writeEnabled;
259
260	// copy name str
261	pDest = (char*) &(pInfo->deviceName);
262	pSrc = (char*) &(pDriverData->name);
263	for( i = 0; i <= 255; i++ )
264		pDest[i] = pSrc[i];
265
266	// remember our local node id
267	pInfo->localNodeID 				= pDriverData->localID;
268	pInfo->fwClientID 				= pDriverData->fwClientID;
269
270	return( error );
271}
272
273//////////////////////////////////////////////
274//
275// DVGetDeviceName
276//
277//   This routine returns the name of a specific DV device
278//
279
280OSErr DVCGetDeviceName( DVCDeviceID deviceID, char * str )
281{
282	DVDriverDataPtr				pDVDriverData;
283	OSErr						error = noErr;
284	short						i;
285
286	// make sure we've got a valid deviceID
287	error = DVIsValidID( (DVDriverID) deviceID );
288	if( error )
289		return( error );
290
291	// extract our driver data pointer from our supposedly opaque reference
292	pDVDriverData = (DVDriverDataPtr) deviceID;
293
294	// copy name str
295	for( i = 0; i <= 255; i++ )
296		str[i] = pDVDriverData->name[i];
297
298	return( error );
299}
300
301//////////////////////////////////////////////
302//
303// DVSetDeviceName
304//
305//   This routine sets the name of a specific DV device
306//
307
308OSErr DVCSetDeviceName( DVCDeviceID deviceID, char * str )
309{
310	DVDriverDataPtr				pDVDriverData;
311	OSErr						error = noErr;
312	short						i;
313
314	// make sure we've got a valid deviceID
315	error = DVIsValidID( (DVDriverID) deviceID );
316	if( error )
317		return( error );
318
319	// extract our driver data pointer from our supposedly opaque reference
320	pDVDriverData = (DVDriverDataPtr) deviceID;
321
322	// copy name str
323	for( i = 0; i <= 255; i++ )
324		pDVDriverData->name[i] = str[i];
325
326//��� We aren't going to support DVNames in this version of Oxcart
327//	// add name to prefs file (if add fails, don't pass error code back up to client)
328//	DVAddName( &(pDVDriverData->uniqueID), pDVDriverData->name );
329
330	return( error );
331}
332
333//////////////////////////////////////////////
334//
335// DVOpenDeviceConnection
336//
337//   This routine opens a connection to a particular driver
338//
339
340OSErr DVCOpenDeviceConnection( DVCDeviceID deviceID, DVCDeviceConnectionID *pConnID )
341{
342	DVDriverDataPtr		pDVDriverData;
343	OSErr				error = noErr;
344
345	// make sure we've got a valid deviceID
346	error = DVIsValidID( (DVDriverID) deviceID );
347	if( error )
348		return( error );
349
350	// extract our driver data pointer from our supposedly opaque reference
351	pDVDriverData = (DVDriverDataPtr) deviceID;
352
353	// don't bother continuing if we're disconnected
354	if( pDVDriverData->deviceDisconnected )
355		return( kDVDisconnectedErr );
356
357	// add a connection
358	pDVDriverData->numConnections++;
359
360	// this is redundant, but we'll do it so we can possibly enhance
361	// multiple client support sometime in the future.
362	*pConnID = (DVCDeviceConnectionID) deviceID;
363
364	//zzz could actually open driver here, but advantage would probably be minimal...
365
366	return( error );
367}
368
369//////////////////////////////////////////////
370//
371// DVCloseDeviceConnection
372//
373//   This routine opens a connection to a particular driver
374//
375
376OSErr DVCCloseDeviceConnection( DVCDeviceConnectionID connID )
377{
378	DVDriverDataPtr		pDVDriverData;
379	OSErr				error = noErr;
380
381	// make sure we've got a valid deviceID
382	error = DVIsValidID( (DVDriverID) connID );
383	if( error )
384		return( error );
385
386	// extract our driver data pointer from our supposedly opaque reference
387	pDVDriverData = (DVDriverDataPtr) connID;
388
389	// remove a connection
390	pDVDriverData->numConnections--;
391
392	//zzz for apple events managing driver disposal here would be good
393	if( ( pDVDriverData->numConnections == 0 ) && (  pDVDriverData->deviceDisconnected ) )
394		DVDisposeDriver( (DVDriverID) connID );
395
396	return( error );
397}
398
399//////////////////////////////////////////////
400//
401// DVGetDeviceClock
402//
403//   This routine opens a connection to a particular driver
404//
405
406OSErr DVCGetDeviceClock( DVCDeviceID deviceID, Component *clock )
407{
408	*clock = ((DVDriverDataPtr)deviceID)->clock;
409
410	return( noErr );
411}
412
413
414#pragma mark -
415#pragma mark ���������� DV Event Notification Calls ����������
416
417///////////////////////////////////////////////////////////////////////
418//
419// DVNewNotification
420//
421//	create new notification record
422//
423OSErr DVCNewNotification( DVCDeviceConnectionID connID, DVCNotifyProc notifyProc,
424						void *userData, DVCNotificationID *pNotifyID )
425{
426	DVNotificationEntryPtr	pEntry;
427	DVCDeviceID				deviceID;
428	OSErr					error = noErr;
429
430	// check the parameters
431	if ( notifyProc == nil )
432		error = paramErr;
433
434	// create new entry
435	if ( error == noErr )
436	{
437		pEntry = (DVNotificationEntryPtr)
438				PoolAllocateResident( sizeof( DVNotificationEntry ), true );
439		if ( pEntry == nil )
440			error = memFullErr;
441	}
442
443	// get the deviceID from the connectionID
444	deviceID = (DVCDeviceID) connID;
445
446	// fill it out
447	if ( error == noErr )
448	{
449		pEntry->deviceID						= deviceID;
450		pEntry->wantedEvents					= nil;
451		pEntry->notifyProc						= notifyProc;
452		pEntry->userRefCon						= userData;
453
454		*pNotifyID = (DVCNotificationID) pEntry;	// notification id
455
456		// put new entry at the back of the line
457		error = PBEnqueueLast( (QElemPtr) pEntry, gpFamilyGlobals->notificationQueue );
458	}
459
460	return error;
461}
462
463//////////////////////////////////////////////
464//
465// DVNotifyMeWhen
466//
467//	activate notification
468//
469OSErr DVCNotifyMeWhen( DVCDeviceConnectionID connID, DVCNotificationID notifyID, UInt32 events)
470{
471	DVNotificationEntryPtr	pEntry;
472	DVCDeviceID				deviceID;
473	OSErr					error = noErr;
474
475	// check the parameters
476	if ( events & kDVEveryEvent == nil )
477			error = paramErr;
478
479	// get the deviceID from the connectionID
480	deviceID = (DVCDeviceID) connID;
481
482	if ( error == noErr )
483	{
484		pEntry = (DVNotificationEntryPtr) notifyID;
485
486		if ( pEntry != nil )
487		{
488			pEntry->wantedEvents = events;
489			// this is sort of a back door - you can specify any device here
490			pEntry->deviceID = deviceID;
491		}
492		else
493			error = paramErr;
494	}
495
496	return error;
497}
498
499//////////////////////////////////////////////
500//
501// DVCancelNotification
502//
503//	deactivate notification
504//
505OSErr DVCCancelNotification( DVCDeviceConnectionID connID, DVCNotificationID notifyID )
506{
507	DVNotificationEntryPtr	pEntry;
508	OSErr					error = noErr;
509	DVEventEntryPtr			pEventEntry;
510
511	pEntry = (DVNotificationEntryPtr) notifyID;
512
513	if ( pEntry != nil )
514	{
515		// don't notify this guy
516		pEntry->wantedEvents = 0L;
517
518		// check the queue to make sure he's not about to be notified.
519		pEventEntry = (DVEventEntryPtr) gpFamilyGlobals->receivedDVEvents->qHead;
520		while ( pEventEntry )
521		{
522			if ( pEventEntry->eventRec.eventHeader.notifID == notifyID )
523				PBDequeue( (QElemPtr) pEventEntry, gpFamilyGlobals->receivedDVEvents );
524				// could be in the queue more than once, so keep going...
525
526			pEventEntry = (DVEventEntryPtr) pEventEntry->qLink;
527		}
528	}
529	else
530		error = paramErr;
531
532	return error;
533}
534
535//////////////////////////////////////////////
536//
537// DVDisposeNotification
538//
539//	remove notification from list
540//
541OSErr DVCDisposeNotification( DVCDeviceConnectionID connID, DVCNotificationID notifyID )
542{
543	DVNotificationEntryPtr	pEntry;
544	OSErr					error = noErr;
545
546	// we're not going to do a check, but it's REAL important not to call
547	// this function from anywhere but task level.
548
549	// go find the entry and remove it from the list
550	pEntry = (DVNotificationEntryPtr) notifyID;
551
552	if ( pEntry != nil )
553	{
554		PBDequeue( (QElemPtr) pEntry, gpFamilyGlobals->notificationQueue );
555		PoolDeallocate( (void*) pEntry );
556	}
557	else
558		error = paramErr;
559
560	return error;
561}
562
563
564#pragma mark -
565#pragma mark ���������� DV Isoch Read Calls ����������
566
567//////////////////////////////////////////////
568//
569// DVEnableRead
570//
571//   This routine initializes the driver for input
572//
573
574OSErr DVCEnableRead( DVCDeviceConnectionID connID )
575{
576	DVBasicCmdParams			intParams;
577	OSErr						error = noErr;
578	DVDriverDataPtr				pDVDriverData;
579	DVCEventRecord				theEvent;
580
581	// make sure we've got a valid driverID
582	error = DVIsValidID( (DVDriverID) connID );
583	if( error )
584		return( error );
585
586	// extract our driver data pointer from our supposedly opaque reference
587	pDVDriverData = (DVDriverDataPtr) connID;
588
589	// make sure we're not already write enabled
590	if ( pDVDriverData->writeEnabled )
591		return ( kAlreadyEnabledErr );
592
593	// add a connection
594	pDVDriverData->numReaders++;
595
596	// if this is the first connection, call the driver
597	// to set the read up.
598	if ( pDVDriverData->numReaders == 1 )
599	{
600		// set up driver's enable isoch read parameters
601		intParams.interfaceSelector = kDVCEnableIsochRead;
602
603		error = DVCallDriver( (DVDriverID) connID, (Ptr) &intParams );
604
605		if ( error == noErr )
606			pDVDriverData->readEnabled = true;
607	}
608
609	if ( error == noErr )
610	{
611		// post a DV event to let the curious know...
612		theEvent.eventHeader.deviceID	= (DVCDeviceID) connID;
613		theEvent.eventHeader.theEvent 	= kDVIsochReadEnabled;
614		DVCPostEvent( &theEvent );
615	}
616
617	return( error );
618}
619
620//////////////////////////////////////////////
621//
622// DVDisableRead
623//
624//   This routine deinitializes the driver for input
625//
626
627OSErr DVCDisableRead( DVCDeviceConnectionID connID )
628{
629	DVBasicCmdParams			intParams;
630	OSErr						error = noErr;
631	DVDriverDataPtr				pDVDriverData;
632	DVCEventRecord				theEvent;
633
634	// make sure we've got a valid driverID
635	error = DVIsValidID( (DVDriverID) connID );
636	if( error )
637		return( error );
638
639	// extract our driver data pointer from our supposedly opaque reference
640	pDVDriverData = (DVDriverDataPtr) connID;
641
642	// delete a connection
643	pDVDriverData->numReaders--;
644
645	// if this is the last connection, call the driver
646	// to tear down the read.
647	if ( pDVDriverData->numReaders == 0 )
648	{
649		// set up driver's disable isoch read parameters
650		intParams.interfaceSelector = kDVCDisableIsochRead;
651
652		error = DVCallDriver( (DVDriverID) connID, (Ptr) &intParams );
653
654		// even if there's an error, we're done
655		pDVDriverData->readEnabled = false;
656	}
657
658	if ( error == noErr )
659	{
660		// post a DV event to let the curious know...
661		theEvent.eventHeader.deviceID	= (DVCDeviceID) connID;
662		theEvent.eventHeader.theEvent 	= kDVIsochReadDisabled;
663		DVCPostEvent( &theEvent );
664	}
665
666	return( error );
667}
668
669//////////////////////////////////////////////
670//
671// DVReadFrame
672//
673//   This routine reads a frame of data from the DV device
674//
675
676OSErr DVCReadFrame( DVCDeviceConnectionID connID, Ptr *ppReadBuffer, UInt32 * pSize )
677{
678	DVGetBufferParams			intParams;
679	OSErr						error = noErr;
680
681	// set up driver's enable isoch read parameters
682	intParams.interfaceSelector = kDVCReadIsochData;
683	intParams.ppBuffer = ppReadBuffer;
684	intParams.pBufferSize = pSize;
685
686	error = DVCallDriver( (DVDriverID) connID, (Ptr) &intParams );
687
688	return( error );
689}
690
691//////////////////////////////////////////////
692//
693// DVReleaseFrame
694//
695//   This routine returns frame of data to the driver for use
696//
697
698OSErr DVCReleaseFrame( DVCDeviceConnectionID connID, Ptr pReadBuffer )
699{
700	DVPassBufferParams			intParams;
701	OSErr						error = noErr;
702
703	// set up driver's enable isoch read parameters
704	intParams.interfaceSelector = kDVCReleaseReadBuffer;
705	intParams.pBuffer = pReadBuffer;
706
707	error = DVCallDriver( (DVDriverID) connID, (Ptr) &intParams );
708
709	return( error );
710}
711
712#pragma mark -
713#pragma mark ���������� DV Isoch Write Calls ����������
714
715//////////////////////////////////////////////
716//
717// DVEnableWrite
718//
719//   This routine initializes the driver for output
720//
721
722OSErr DVCEnableWrite( DVCDeviceConnectionID connID )
723{
724	DVBasicCmdParams			intParams;
725	DVDriverDataPtr				pDVDriverData;
726	OSErr						error = noErr;
727	DVCEventRecord				theEvent;
728
729	// extract our driver data pointer from our supposedly opaque reference
730	pDVDriverData = (DVDriverDataPtr) connID;
731
732	// make sure we're not already enabled for reading or writing
733	if ( pDVDriverData->readEnabled || pDVDriverData->writeEnabled )
734		return ( kAlreadyEnabledErr );
735
736	// set up driver's enable isoch read parameters
737	intParams.interfaceSelector = kDVCEnableIsochWrite;
738
739	error = DVCallDriver( (DVDriverID) connID, (Ptr) &intParams );
740
741	if ( error == noErr )
742	{
743		pDVDriverData->writeEnabled = true;
744
745		// post a DV event to let the curious know...
746		theEvent.eventHeader.deviceID	= (DVCDeviceID) connID;
747		theEvent.eventHeader.theEvent 	= kDVIsochWriteEnabled;
748		DVCPostEvent( &theEvent );
749	}
750
751	return( error );
752
753}
754
755//////////////////////////////////////////////
756//
757// DVDisableWrite
758//
759//   This routine deinitializes the driver for output
760//
761
762OSErr DVCDisableWrite( DVCDeviceConnectionID connID )
763{
764	DVBasicCmdParams			intParams;
765	DVDriverDataPtr				pDVDriverData;
766	OSErr						error = noErr;
767	DVCEventRecord				theEvent;
768
769	// extract our driver data pointer from our supposedly opaque reference
770	pDVDriverData = (DVDriverDataPtr) connID;
771
772	// set up driver's enable isoch read parameters
773	intParams.interfaceSelector = kDVCDisableIsochWrite;
774
775	error = DVCallDriver( (DVDriverID) connID, (Ptr) &intParams );
776
777	// even if there's an error, we're done
778	pDVDriverData->writeEnabled = false;
779
780	// post a DV event to let the curious know...
781	theEvent.eventHeader.deviceID	= (DVCDeviceID) connID;
782	theEvent.eventHeader.theEvent 	= kDVIsochWriteDisabled;
783	DVCPostEvent( &theEvent );
784
785	return( error );
786
787}
788
789//////////////////////////////////////////////
790//
791// DVGetEmptyFrame
792//
793//   This routine retrieves an empty frame from the driver for output
794//
795
796OSErr DVCGetEmptyFrame( DVCDeviceConnectionID connID, Ptr *ppEmptyFrameBuffer, UInt32 * pSize )
797{
798	DVGetBufferParams			intParams;
799	OSErr						error = noErr;
800
801	// set up driver's enable isoch read parameters
802	intParams.interfaceSelector = kDVCGetEmptyFrame;
803	intParams.ppBuffer = ppEmptyFrameBuffer;
804	intParams.pBufferSize = pSize;
805
806	error = DVCallDriver( (DVDriverID) connID, (Ptr) &intParams );
807	return( error );
808}
809
810//////////////////////////////////////////////
811//
812// DVWriteFrame
813//
814//   This routine sends a frame of data to the camera
815//
816
817OSErr DVCWriteFrame( DVCDeviceConnectionID connID, Ptr pWriteBuffer )
818{
819	DVPassBufferParams			intParams;
820	OSErr						error = noErr;
821
822	// set up driver's enable isoch read parameters
823	intParams.interfaceSelector = kDVCWriteFrame;
824	intParams.pBuffer = pWriteBuffer;
825
826	error = DVCallDriver( (DVDriverID) connID, (Ptr) &intParams );
827	return( error );
828}
829
830#pragma mark -
831#pragma mark ���������� AVC Transaction Calls ����������
832
833//////////////////////////////////////////////
834//
835// DVEnableAVCTransactions
836//
837//   This routine initializes the driver for
838//	 performing avc transactions
839//
840OSErr DVCEnableAVCTransactions( DVCDeviceConnectionID connID )
841{
842	OSErr						error = noErr;
843
844	return( error );
845}
846
847//////////////////////////////////////////////
848//
849// DVDoAVCTransaction
850//
851//   This routine sends a transaction block to the driver
852//
853
854OSErr DVCDoAVCTransaction( DVCDeviceConnectionID connID, AVCTransactionParamsPtr pParams )
855{
856	DVAVCTransactionParams		transactionParams;
857	OSErr						error = noErr;
858
859	// fill out the internal tansaction block
860	transactionParams.interfaceSelector 	= kAVCDoTransaction;
861	transactionParams.commandBufferPtr		= pParams->commandBufferPtr;
862	transactionParams.commandLength			= pParams->commandLength;
863	transactionParams.responseBufferPtr		= pParams->responseBufferPtr;
864	transactionParams.responseBufferSize	= pParams->responseBufferSize;
865	transactionParams.responseHandler		= pParams->responseHandler;
866
867	error = DVCallDriver( (DVDriverID) connID, (Ptr) &transactionParams );
868
869	return( error );
870}
871
872//////////////////////////////////////////////
873//
874// DVDisableAVCTransactions
875//
876//   This routine deinitializes the driver for
877//	 performing avc transactions
878//
879OSErr DVCDisableAVCTransactions( DVCDeviceConnectionID connID )
880{
881	OSErr						error = noErr;
882
883	return( error );
884}
885
886#pragma mark -
887#pragma mark ���������� To be discontinued... ����������
888
889//////////////////////////////////////////////
890//
891// DVIsEnabled
892//
893//   This routine tells if this device is enabled
894//
895
896OSErr DVCIsEnabled( DVCDeviceConnectionID connID, Boolean *isEnabled)
897{
898	DVDriverDataPtr		pDVDriverData;
899	OSErr				error = noErr;
900
901	// make sure we've got a valid driverID
902	error = DVIsValidID( (DVDriverID) connID );
903	if( error )
904		return( error );
905
906	pDVDriverData = (DVDriverDataPtr) connID;
907
908	// is it enabled
909	if( pDVDriverData->numConnections > 0)
910		*isEnabled = true;
911	else
912		*isEnabled = false;
913
914	return error;
915}
916
917//////////////////////////////////////////////
918//
919// DVGetDeviceStandard
920//
921//   This routine returns the video standard
922//
923
924OSErr DVCGetDeviceStandard( DVCDeviceConnectionID connID, UInt32 * pStandard )
925{
926	AVCCTSFrameStruct		avcFrame;
927	AVCTransactionParams	transactionParams;
928	UInt8					responseBuffer[ 16 ];
929	OSErr					theErr = noErr;
930	UInt32					currentSignal, AVCStatus;
931
932	// fill up the avc frame
933	avcFrame.cmdType_respCode	= kAVCStatusInquiryCommand;
934	avcFrame.headerAddress 		= 0x20;						// for now
935	avcFrame.opcode 			= kAVCOutputSignalModeOpcode;
936	avcFrame.operand[ 0 ] 		= kAVCSignalModeDummyOperand;
937
938	// fill up the transaction parameter block
939	transactionParams.commandBufferPtr		= (Ptr) &avcFrame;
940	transactionParams.commandLength			= 4;
941	transactionParams.responseBufferPtr		= (Ptr) responseBuffer;
942	transactionParams.responseBufferSize	= 16;
943	transactionParams.responseHandler		= nil;
944
945	theErr = DVCDoAVCTransaction( (DVDriverID) connID, &transactionParams );
946
947	currentSignal = ((responseBuffer[ 2 ] << 8) | responseBuffer[ 3 ]);
948	AVCStatus = responseBuffer[ 0 ];
949
950	*pStandard = kUnknownStandard;
951	switch (currentSignal & 0x000000ff)
952	{
953		case kAVCSignalModeSD525_60:
954		case kAVCSignalModeSDL525_60:
955		case kAVCSignalModeHD1125_60:
956			*pStandard = kNTSCStandard;
957			return( theErr );
958
959		case kAVCSignalModeSD625_50:
960		case kAVCSignalModeSDL625_50:
961		case kAVCSignalModeHD1250_50:
962			*pStandard = kPALStandard;
963			return( theErr );
964
965		default:
966			return( kUnknownStandardErr ); // how should I handle this?
967	}
968}
969
970#pragma mark -
971#pragma mark ���������� Private Interface Calls ����������
972#pragma mark -
973
974///////////////////////////////////////////////////////////////////////
975//
976// DVPostEvent
977//
978//	used for sending the real notification
979//
980///////////////////////////////////////////////////////////////////////
981OSErr DVCPostEvent( DVCEventRecordPtr	pEvent )
982{
983	OSErr					error = noErr;
984
985	// make sure it's a legit event
986	if ( (pEvent->eventHeader.theEvent & kDVEveryEvent) == nil )
987			error = paramErr;
988
989	// get to secondary interrupt level as quickly as possible, where
990	// things are nice and synchronized...
991	error = CallSecondaryInterruptHandler2( (SecondaryInterruptHandler2) PostEventSIH,
992											nil,
993											pEvent,
994											nil );
995
996	// now that we've pre-processed the event, give task level
997	// notifications a chance to run, if appropriate.
998	if ( CurrentExecutionLevel() == kTaskLevel )
999		DVIdle();
1000
1001	return( error );
1002}
1003
1004
1005///////////////////////////////////////////////////////////////////////
1006///////////////////////////////////////////////////////////////////////
1007OSStatus
1008PostEventSIH( void* p1, void* p2 )
1009{
1010	DVCEventRecordPtr			pEvent = (DVCEventRecordPtr) p1;
1011	DVEventEntryPtr				pEventEntry;
1012	DVNotificationEntryPtr		pEntry;
1013	OSErr						error = noErr;
1014
1015	// We now have two broad classifications of events - ones that need to be
1016	// reported ASAP, which are stream related:
1017	//
1018	// 		kDVIsochReadComplete
1019	//		kDVIsochWriteComplete
1020	//
1021	// and ones that are device management related, whose notifications will
1022	// probably generate massive amounts of task-level only Toolbox calls:
1023	//
1024	//		kDVDeviceAdded
1025	//		kDVDeviceRemoved
1026	//		kDVIsochReadEnabled
1027	//		kDVIsochReadDisabled
1028	//		kDVIsochWriteEnabled
1029	//		kDVIsochWriteDisabled
1030	//
1031	// We ship the low-latency notifications to secondary interrupt, while
1032	// the task level calls we queue and get back to them when someone
1033	// calls DVIdle().
1034	//
1035
1036	// ok, so let's go find out who's waiting for this event
1037
1038	// go through list looking for the curious
1039	pEntry = (DVNotificationEntryPtr) gpFamilyGlobals->notificationQueue->qHead;
1040	while ( pEntry != nil )
1041	{
1042		if ( (pEvent->eventHeader.theEvent & pEntry->wantedEvents) != nil )
1043		{
1044			// only send notification if it's a global connection id or if
1045			// the event came from the same deviceID as this notif entry
1046			if ( (pEntry->deviceID == kDVGlobalEventConnectionID) ||
1047				(pEvent->eventHeader.deviceID == pEntry->deviceID) )
1048			{
1049				// we currently only support a one-shot notification, like clock callbacks
1050				pEntry->wantedEvents = nil;
1051
1052				// make sure the event contains this notification id
1053				pEvent->eventHeader.notifID = (DVCNotificationID) pEntry;
1054
1055
1056				// check before calling..
1057				switch( pEvent->eventHeader.theEvent )
1058				{
1059					case kDVIsochReadComplete:
1060					case kDVIsochWriteComplete:
1061						// process event immediately...
1062						error = (*pEntry->notifyProc)( pEvent, pEntry->userRefCon );
1063						break;
1064
1065					case kDVDeviceAdded:
1066					case kDVDeviceRemoved:
1067					case kDVIsochReadEnabled:
1068					case kDVIsochReadDisabled:
1069					case kDVIsochWriteEnabled:
1070					case kDVIsochWriteDisabled:
1071						// queue the event and proc for later processing...
1072
1073						// get an entry
1074						error = PBDequeueFirst( gpFamilyGlobals->availableDVEvents,
1075												(QElemPtr*) &pEventEntry );
1076
1077						// if we don't have any more available event elements,
1078						// we just drop the events on the floor
1079
1080						// copy the notify proc & refcon
1081						if ( error == noErr )
1082						{
1083							pEventEntry->notifyProc	= pEntry->notifyProc;
1084							pEventEntry->userRefCon = pEntry->userRefCon;
1085						}
1086
1087						// copy the event
1088						if ( error == noErr )
1089							BlockCopy( pEvent, &(pEventEntry->eventRec), sizeof( DVCEventRecord ) );
1090
1091						// queue it
1092						if ( error == noErr )
1093							PBEnqueue( (QElemPtr) pEventEntry, gpFamilyGlobals->receivedDVEvents );
1094
1095						// If we haven't already sent notification
1096						// to Notification Mgr to run tasks, do it now...
1097						if ( CompareAndSwap( false, true, &(gpFamilyGlobals->nmIsInstalled) ) )
1098							NMInstall( &(gpFamilyGlobals->dvNMRec) );
1099
1100						break;
1101
1102					default:
1103						break;
1104				}
1105
1106			}
1107		}
1108
1109		// next entry
1110		pEntry = (DVNotificationEntryPtr) pEntry->qLink;
1111	}
1112
1113	return( error );
1114}
1115
1116
1117///////////////////////////////////////////////////////////////////////
1118///////////////////////////////////////////////////////////////////////
1119OSErr
1120DVIdle( void )
1121{
1122	DVEventEntryPtr		pEventEntry;
1123	OSErr				error = noErr;
1124
1125	// this routine should only get called at task level
1126
1127	// go see if there are any task level notifications that
1128	// need to be done. we take from the back end since that
1129	// was the oldest one.
1130	while( true )
1131	{
1132		error = PBDequeueLast( gpFamilyGlobals->receivedDVEvents,
1133								(QElemPtr*) &pEventEntry );
1134
1135		if ( error == noErr )
1136		{
1137			if ( pEventEntry->notifyProc )
1138			{
1139				// process it...
1140				error = (*pEventEntry->notifyProc)( &(pEventEntry->eventRec), pEventEntry->userRefCon );
1141			}
1142
1143			// and put it back into available queue
1144			PBEnqueue( (QElemPtr) pEventEntry, gpFamilyGlobals->availableDVEvents );
1145		}
1146		else
1147			// we're out of events (probably)
1148			break;
1149	}
1150
1151	return( error );
1152}
1153
1154
1155///////////////////////////////////////////////////////////////////////
1156///////////////////////////////////////////////////////////////////////
1157void
1158myNMHandler( NMRecPtr pNM )
1159{
1160	do {
1161		DVIdle();
1162	} while( CompareAndSwap( true, false, &(gpFamilyGlobals->nmIsInstalled) ) );
1163
1164	// until next time...
1165	NMRemove( pNM );
1166}
1167
1168
1169
1170