1// ****************************************************************************
2//
3//  	CDspCommObject.cpp
4//
5//		Implementation file for EchoGals generic driver DSP interface class.
6//
7// ----------------------------------------------------------------------------
8//
9// This file is part of Echo Digital Audio's generic driver library.
10// Copyright Echo Digital Audio Corporation (c) 1998 - 2005
11// All rights reserved
12// www.echoaudio.com
13//
14// This library is free software; you can redistribute it and/or
15// modify it under the terms of the GNU Lesser General Public
16// License as published by the Free Software Foundation; either
17// version 2.1 of the License, or (at your option) any later version.
18//
19// This library is distributed in the hope that it will be useful,
20// but WITHOUT ANY WARRANTY; without even the implied warranty of
21// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22// Lesser General Public License for more details.
23//
24// You should have received a copy of the GNU Lesser General Public
25// License along with this library; if not, write to the Free Software
26// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
27//
28// ****************************************************************************
29
30#include "CEchoGals.h"
31
32#ifdef DSP_56361
33#include "LoaderDSP.c"
34#endif
35
36#define COMM_PAGE_PHYS_BYTES	((sizeof(DspCommPage)+PAGE_SIZE-1)/PAGE_SIZE)*PAGE_SIZE
37
38
39/****************************************************************************
40
41	Construction and destruction
42
43 ****************************************************************************/
44
45//===========================================================================
46//
47// Overload new & delete so memory for this object is allocated
48//	from non-paged memory.
49//
50//===========================================================================
51
52PVOID CDspCommObject::operator new( size_t Size )
53{
54	PVOID 		pMemory;
55	ECHOSTATUS 	Status;
56
57	Status = OsAllocateNonPaged(Size,&pMemory);
58
59	if ( (ECHOSTATUS_OK != Status) || (NULL == pMemory ))
60	{
61		ECHO_DEBUGPRINTF(("CDspCommObject::operator new - memory allocation failed\n"));
62
63		pMemory = NULL;
64	}
65	else
66	{
67		memset( pMemory, 0, Size );
68	}
69
70	return pMemory;
71
72}	// PVOID CDspCommObject::operator new( size_t Size )
73
74
75VOID  CDspCommObject::operator delete( PVOID pVoid )
76{
77	if ( ECHOSTATUS_OK != OsFreeNonPaged( pVoid ) )
78	{
79		ECHO_DEBUGPRINTF( ("CDspCommObject::operator delete memory free "
80								 "failed\n") );
81	}
82}	// VOID  CDspCommObject::operator delete( PVOID pVoid )
83
84
85//===========================================================================
86//
87// Constructor
88//
89//===========================================================================
90
91CDspCommObject::CDspCommObject
92(
93	PDWORD		pdwDspRegBase,				// Virtual ptr to DSP registers
94	PCOsSupport	pOsSupport
95)
96{
97	INT32	i;
98
99	ECHO_ASSERT(pOsSupport );
100
101	//
102	// Init all the basic stuff
103	//
104	strcpy( m_szCardName, "??????" );
105	m_pOsSupport = pOsSupport;				// Ptr to OS Support methods & data
106	m_pdwDspRegBase = pdwDspRegBase;		// Virtual addr DSP's register base
107	m_bBadBoard = TRUE;						// Set TRUE until DSP loaded
108	m_pwDspCode = NULL;						// Current DSP code not loaded
109	m_byDigitalMode = DIGITAL_MODE_NONE;
110	m_wInputClock = ECHO_CLOCK_INTERNAL;
111	m_wOutputClock = ECHO_CLOCK_WORD;
112	m_ullLastLoadAttemptTime = (ULONGLONG)(DWORD)(0L - DSP_LOAD_ATTEMPT_PERIOD);	// force first load to go
113
114#ifdef MIDI_SUPPORT
115	m_ullNextMidiWriteTime = 0;
116#endif
117
118	//
119	// Create the DSP comm page - this is the area of memory read and written by
120	// the DSP via bus mastering
121	//
122	ECHOSTATUS Status;
123	DWORD dwSegmentSize;
124	PHYS_ADDR PhysAddr;
125
126	Status = pOsSupport->AllocPhysPageBlock(	COMM_PAGE_PHYS_BYTES,
127															m_pDspCommPageBlock);
128	if (ECHOSTATUS_OK != Status)
129	{
130		ECHO_DEBUGPRINTF( ("CDspCommObject::CDspCommObject DSP comm page "
131								 "memory allocation failed\n") );
132		return;
133	}
134
135	m_pDspCommPage = (PDspCommPage) pOsSupport->
136													GetPageBlockVirtAddress( m_pDspCommPageBlock );
137
138	pOsSupport->GetPageBlockPhysSegment(m_pDspCommPageBlock,
139													0,
140													PhysAddr,
141													dwSegmentSize);
142	m_dwCommPagePhys = PhysAddr;
143
144	//
145	// Init the comm page
146	//
147	m_pDspCommPage->dwCommSize = SWAP( sizeof( DspCommPage ) );
148													// Size of DSP comm page
149
150	m_pDspCommPage->dwHandshake = 0xffffffff;
151	m_pDspCommPage->dwMidiOutFreeCount = SWAP( (DWORD) DSP_MIDI_OUT_FIFO_SIZE );
152
153	for ( i = 0; i < DSP_MAXAUDIOINPUTS; i++ )
154		m_pDspCommPage->InLineLevel[ i ] = 0x00;
155													// Set line levels so we don't blast
156													// any inputs on startup
157	memset( m_pDspCommPage->byMonitors,
158			  GENERIC_TO_DSP(ECHOGAIN_MUTED),
159			  MONITOR_ARRAY_SIZE );			// Mute all monitors
160
161	memset( m_pDspCommPage->byVmixerLevel,
162			  GENERIC_TO_DSP(ECHOGAIN_MUTED),
163			  VMIXER_ARRAY_SIZE );			// Mute all virtual mixer levels
164
165#ifdef DIGITAL_INPUT_AUTO_MUTE_SUPPORT
166
167	m_fDigitalInAutoMute = TRUE;
168
169#endif
170
171}	// CDspCommObject::CDspCommObject
172
173
174//===========================================================================
175//
176// Destructor
177//
178//===========================================================================
179
180CDspCommObject::~CDspCommObject()
181{
182	//
183	// Go to sleep
184	//
185	GoComatose();
186
187	//
188	// Free the comm page
189	//
190	if ( NULL != m_pDspCommPageBlock )
191	{
192		m_pOsSupport->FreePhysPageBlock( COMM_PAGE_PHYS_BYTES,
193													m_pDspCommPageBlock);
194	}
195
196	ECHO_DEBUGPRINTF( ( "CDspCommObject::~CDspCommObject() is toast!\n" ) );
197
198}	// CDspCommObject::~CDspCommObject()
199
200
201
202
203/****************************************************************************
204
205	Firmware loading functions
206
207 ****************************************************************************/
208
209//===========================================================================
210//
211// ASIC status check - some cards have one or two ASICs that need to be
212// loaded.  Once that load is complete, this function is called to see if
213// the load was successful.
214//
215// If this load fails, it does not necessarily mean that the hardware is
216// defective - the external box may be disconnected or turned off.
217//
218//===========================================================================
219
220BOOL CDspCommObject::CheckAsicStatus()
221{
222	DWORD	dwAsicStatus;
223	DWORD	dwReturn;
224
225	//
226	// Always succeed if this card doesn't have an ASIC
227	//
228	if ( !m_bHasASIC )
229	{
230		m_bASICLoaded = TRUE;
231		return TRUE;
232	}
233
234	// Send the vector command
235	m_bASICLoaded = FALSE;
236	SendVector( DSP_VC_TEST_ASIC );
237
238	// The DSP will return a value to indicate whether or not the
239	// ASIC is currently loaded
240	dwReturn = Read_DSP( &dwAsicStatus );
241	if ( ECHOSTATUS_OK != dwReturn )
242	{
243		ECHO_DEBUGPRINTF(("CDspCommObject::CheckAsicStatus - failed on Read_DSP\n"));
244		ECHO_DEBUGBREAK();
245		return FALSE;
246	}
247
248#ifdef ECHO_DEBUG
249	if ( (dwAsicStatus != ASIC_LOADED) && (dwAsicStatus != ASIC_NOT_LOADED) )
250	{
251		ECHO_DEBUGBREAK();
252	}
253#endif
254
255	if ( dwAsicStatus == ASIC_LOADED )
256		m_bASICLoaded = TRUE;
257
258	return m_bASICLoaded;
259
260}	// BOOL CDspCommObject::CheckAsicStatus()
261
262
263//===========================================================================
264//
265//	Load ASIC code - done after the DSP is loaded
266//
267//===========================================================================
268
269BOOL CDspCommObject::LoadASIC
270(
271	DWORD	dwCmd,
272	PBYTE	pCode,
273	DWORD	dwSize
274)
275{
276	DWORD i;
277#ifdef _WIN32
278	DWORD dwChecksum = 0;
279#endif
280
281	ECHO_DEBUGPRINTF(("CDspCommObject::LoadASIC\n"));
282
283	if ( !m_bHasASIC )
284		return TRUE;
285
286#ifdef _DEBUG
287	ULONGLONG	ullStartTime, ullCurTime;
288	m_pOsSupport->OsGetSystemTime( &ullStartTime );
289#endif
290
291	// Send the "Here comes the ASIC" command
292	if ( ECHOSTATUS_OK != Write_DSP( dwCmd ) )
293		return FALSE;
294
295	// Write length of ASIC file in bytes
296	if ( ECHOSTATUS_OK != Write_DSP( dwSize ) )
297		return FALSE;
298
299	for ( i = 0; i < dwSize; i++ )
300	{
301#ifdef _WIN32
302		dwChecksum += pCode[i];
303#endif
304
305		if ( ECHOSTATUS_OK != Write_DSP( pCode[ i ] ) )
306		{
307			ECHO_DEBUGPRINTF(("\tfailed on Write_DSP\n"));
308			return FALSE;
309		}
310	}
311
312#ifdef _DEBUG
313	m_pOsSupport->OsGetSystemTime( &ullCurTime );
314	ECHO_DEBUGPRINTF( ("CDspCommObject::LoadASIC took %ld usec.\n",
315							(ULONG) ( ullCurTime - ullStartTime ) ) );
316	ECHO_DEBUGPRINTF(("ASIC load OK\n"));
317#endif
318
319#if defined(_WIN32) && (DBG)
320	DbgPrint("--- ASIC checksum is 0x%lx\n",dwChecksum);
321#endif
322
323	return TRUE;
324
325}	// BOOL CDspCommObject::LoadASIC( DWORD dwCmd, PBYTE pCode, DWORD dwSize )
326
327
328//===========================================================================
329//
330// InstallResidentLoader
331//
332// Install the resident loader for 56361 DSPs;  The resident loader
333// is on the EPROM on the board for 56301 DSP.
334//
335// The resident loader is a tiny little program that is used to load
336// the real DSP code.
337//
338//===========================================================================
339
340#ifdef DSP_56361
341
342ECHOSTATUS CDspCommObject::InstallResidentLoader()
343{
344	DWORD			dwAddress;
345	DWORD			dwIndex;
346	INT32			iNum;
347	INT32			i;
348	DWORD			dwReturn;
349	PWORD			pCode;
350
351	ECHO_DEBUGPRINTF( ("CDspCommObject::InstallResidentLoader\n") );
352
353	//
354	// 56361 cards only!
355	//
356	if (DEVICE_ID_56361 != m_pOsSupport->GetDeviceId() )
357		return ECHOSTATUS_OK;
358
359	//
360	// Look to see if the resident loader is present.  If the resident loader
361	// is already installed, host flag 5 will be on.
362	//
363	DWORD dwStatus;
364	dwStatus = GetDspRegister( CHI32_STATUS_REG );
365	if ( 0 != (dwStatus & CHI32_STATUS_REG_HF5 ) )
366	{
367		ECHO_DEBUGPRINTF(("\tResident loader already installed; status is 0x%lx\n",
368								dwStatus));
369		return ECHOSTATUS_OK;
370	}
371	//
372	// Set DSP format bits for 24 bit mode
373	//
374	SetDspRegister( CHI32_CONTROL_REG,
375						 GetDspRegister( CHI32_CONTROL_REG ) | 0x900 );
376
377	//---------------------------------------------------------------------------
378	//
379	// Loader
380	//
381	// The DSP code is an array of 16 bit words.  The array is divided up into
382	// sections.  The first word of each section is the size in words, followed
383	// by the section type.
384	//
385	// Since DSP addresses and data are 24 bits wide, they each take up two
386	// 16 bit words in the array.
387	//
388	// This is a lot like the other loader loop, but it's not a loop,
389	// you don't write the memory type, and you don't write a zero at the end.
390	//
391	//---------------------------------------------------------------------------
392
393	pCode = pwLoaderDSP;
394	//
395	// Skip the header section; the first word in the array is the size of
396	//	the first section, so the first real section of code is pointed to
397	//	by pCode[0].
398	//
399	dwIndex = pCode[ 0 ];
400	//
401	// Skip the section size, LRS block type, and DSP memory type
402	//
403	dwIndex += 3;
404	//
405	// Get the number of DSP words to write
406	//
407	iNum = pCode[ dwIndex++ ];
408	//
409	// Get the DSP address for this block; 24 bits, so build from two words
410	//
411	dwAddress = ( pCode[ dwIndex ] << 16 ) + pCode[ dwIndex + 1 ];
412	dwIndex += 2;
413	//
414	// Write the count to the DSP
415	//
416	dwReturn = Write_DSP( (DWORD) iNum );
417	if ( dwReturn != 0 )
418	{
419		ECHO_DEBUGPRINTF( ("CDspCommObject::InstallResidentLoader: Failed to "
420								 "write word count!\n") );
421		return ECHOSTATUS_DSP_DEAD;
422	}
423
424	// Write the DSP address
425	dwReturn = Write_DSP( dwAddress );
426	if ( dwReturn != 0 )
427	{
428		ECHO_DEBUGPRINTF( ("CDspCommObject::InstallResidentLoader: Failed to "
429								 "write DSP address!\n") );
430		return ECHOSTATUS_DSP_DEAD;
431	}
432
433
434	// Write out this block of code to the DSP
435	for ( i = 0; i < iNum; i++) //
436	{
437		DWORD	dwData;
438
439		dwData = ( pCode[ dwIndex ] << 16 ) + pCode[ dwIndex + 1 ];
440		dwReturn = Write_DSP( dwData );
441		if ( dwReturn != 0 )
442		{
443			ECHO_DEBUGPRINTF( ("CDspCommObject::InstallResidentLoader: Failed to "
444									 "write DSP code\n") );
445			return ECHOSTATUS_DSP_DEAD;
446		}
447
448		dwIndex+=2;
449	}
450
451	//
452	// Wait for flag 5 to come up
453	//
454	BOOL			fSuccess;
455	ULONGLONG 	ullCurTime,ullTimeout;
456
457	m_pOsSupport->OsGetSystemTime( &ullCurTime );
458	ullTimeout = ullCurTime + 10000L;		// 10m.s.
459	fSuccess = FALSE;
460	do
461	{
462		m_pOsSupport->OsSnooze(50);	// Give the DSP some time;
463														// no need to hog the CPU
464
465		dwStatus = GetDspRegister( CHI32_STATUS_REG );
466		if (0 != (dwStatus & CHI32_STATUS_REG_HF5))
467		{
468			fSuccess = TRUE;
469			break;
470		}
471
472		m_pOsSupport->OsGetSystemTime( &ullCurTime );
473
474	} while (ullCurTime < ullTimeout);
475
476	if (FALSE == fSuccess)
477	{
478		ECHO_DEBUGPRINTF(("\tResident loader failed to set HF5\n"));
479		return ECHOSTATUS_DSP_DEAD;
480	}
481
482	ECHO_DEBUGPRINTF(("\tResident loader successfully installed\n"));
483
484	return ECHOSTATUS_OK;
485
486}	// ECHOSTATUS CDspCommObject::InstallResidentLoader()
487
488#endif // DSP_56361
489
490
491//===========================================================================
492//
493// LoadDSP
494//
495// This loads the DSP code.
496//
497//===========================================================================
498
499ECHOSTATUS CDspCommObject::LoadDSP
500(
501	PWORD	pCode					// Ptr to DSP object code
502)
503{
504	DWORD			dwAddress;
505	DWORD			dwIndex;
506	INT32			iNum;
507	INT32			i;
508	DWORD			dwReturn;
509	ULONGLONG	ullTimeout, ullCurTime;
510	ECHOSTATUS	Status;
511
512	ECHO_DEBUGPRINTF(("CDspCommObject::LoadDSP\n"));
513	if ( m_pwDspCode == pCode )
514	{
515		ECHO_DEBUGPRINTF( ("\tDSP is already loaded!\n") );
516		return ECHOSTATUS_FIRMWARE_LOADED;
517	}
518	m_bBadBoard = TRUE;		// Set TRUE until DSP loaded
519	m_pwDspCode = NULL;		// Current DSP code not loaded
520	m_bASICLoaded = FALSE;	// Loading the DSP code will reset the ASIC
521
522	ECHO_DEBUGPRINTF(("CDspCommObject::LoadDSP  Set m_bBadBoard to TRUE\n"));
523
524	//
525	//	If this board requires a resident loader, install it.
526	//
527#ifdef DSP_56361
528	InstallResidentLoader();
529#endif
530
531	// Send software reset command
532	if ( ECHOSTATUS_OK != SendVector( DSP_VC_RESET ) )
533	{
534		m_pOsSupport->EchoErrorMsg(
535			"CDspCommObject::LoadDsp SendVector DSP_VC_RESET failed",
536			"Critical Failure" );
537		return ECHOSTATUS_DSP_DEAD;
538	}
539
540	// Delay 10us
541	m_pOsSupport->OsSnooze( 10L );
542
543	// Wait 10ms for HF3 to indicate that software reset is complete
544	m_pOsSupport->OsGetSystemTime( &ullCurTime );
545	ullTimeout = ullCurTime + 10000L;		// 10m.s.
546
547	// wait for HF3 to be set
548wait_for_hf3:
549
550	if ( GetDspRegister( CHI32_STATUS_REG ) & CHI32_STATUS_REG_HF3 )
551			goto set_dsp_format_bits;
552		m_pOsSupport->OsGetSystemTime( &ullCurTime );
553		if ( ullCurTime > ullTimeout)
554		{
555			ECHO_DEBUGPRINTF( ("CDspCommObject::LoadDSP Timeout waiting for "
556									 "CHI32_STATUS_REG_HF3\n") );
557			m_pOsSupport->EchoErrorMsg(
558				"CDspCommObject::LoadDSP SendVector DSP_VC_RESET failed",
559				"Critical Failure" );
560			return ECHOSTATUS_DSP_TIMEOUT;
561		}
562	goto wait_for_hf3;
563
564
565	// Set DSP format bits for 24 bit mode now that soft reset is done
566set_dsp_format_bits:
567		SetDspRegister( CHI32_CONTROL_REG,
568						 GetDspRegister( CHI32_CONTROL_REG ) | (DWORD) 0x900 );
569
570	//---------------------------------------------------------------------------
571	// Main loader loop
572	//---------------------------------------------------------------------------
573
574	dwIndex = pCode[ 0 ];
575
576	for (;;)
577	{
578		INT32	iBlockType;
579		INT32	iMemType;
580
581		// Total Block Size
582		dwIndex++;
583
584		// Block Type
585		iBlockType = pCode[ dwIndex ];
586		if ( iBlockType == 4 )  // We're finished
587			break;
588
589		dwIndex++;
590
591		// Memory Type  P=0,X=1,Y=2
592		iMemType = pCode[ dwIndex ];
593		dwIndex++;
594
595		// Block Code Size
596		iNum = pCode[ dwIndex ];
597		dwIndex++;
598		if ( iNum == 0 )			// We're finished
599			break;
600
601 		// Start Address
602		dwAddress = ( (DWORD) pCode[ dwIndex ] << 16 ) + pCode[ dwIndex + 1 ];
603//		ECHO_DEBUGPRINTF( ("\tdwAddress %lX\n", dwAddress) );
604		dwIndex += 2;
605
606		dwReturn = Write_DSP( (DWORD)iNum );
607		if ( dwReturn != 0 )
608		{
609			ECHO_DEBUGPRINTF(("LoadDSP - failed to write number of DSP words\n"));
610			return ECHOSTATUS_DSP_DEAD;
611		}
612
613		dwReturn = Write_DSP( dwAddress );
614		if ( dwReturn != 0 )
615		{
616			ECHO_DEBUGPRINTF(("LoadDSP - failed to write DSP address\n"));
617			return ECHOSTATUS_DSP_DEAD;
618		}
619
620		dwReturn = Write_DSP( (DWORD)iMemType );
621		if ( dwReturn != 0 )
622		{
623			ECHO_DEBUGPRINTF(("LoadDSP - failed to write DSP memory type\n"));
624			return ECHOSTATUS_DSP_DEAD;
625		}
626
627		// Code
628		for ( i = 0; i < iNum; i++ )
629		{
630			DWORD	dwData;
631
632			dwData = ( (DWORD) pCode[ dwIndex ] << 16 ) + pCode[ dwIndex + 1 ];
633			dwReturn = Write_DSP( dwData );
634			if ( dwReturn != 0 )
635			{
636				ECHO_DEBUGPRINTF(("LoadDSP - failed to write DSP data\n"));
637				return ECHOSTATUS_DSP_DEAD;
638			}
639
640			dwIndex += 2;
641		}
642//		ECHO_DEBUGPRINTF( ("\tEnd Code Block\n") );
643	}
644	dwReturn = Write_DSP( 0 );					// We're done!!!
645	if ( dwReturn != 0 )
646	{
647		ECHO_DEBUGPRINTF(("LoadDSP: Failed to write final zero\n"));
648		return ECHOSTATUS_DSP_DEAD;
649	}
650
651
652	// Delay 10us
653	m_pOsSupport->OsSnooze( 10L );
654
655	m_pOsSupport->OsGetSystemTime( &ullCurTime );
656	ullTimeout  = ullCurTime + 500000L;		// 1/2 sec. timeout
657
658	while ( ullCurTime <= ullTimeout)
659	{
660		//
661		// Wait for flag 4 - indicates that the DSP loaded OK
662		//
663		if ( GetDspRegister( CHI32_STATUS_REG ) & CHI32_STATUS_REG_HF4 )
664		{
665			SetDspRegister( CHI32_CONTROL_REG,
666								 GetDspRegister( CHI32_CONTROL_REG ) & ~0x1b00 );
667
668			dwReturn = Write_DSP( DSP_FNC_SET_COMMPAGE_ADDR );
669			if ( dwReturn != 0 )
670			{
671				ECHO_DEBUGPRINTF(("LoadDSP - Failed to write DSP_FNC_SET_COMMPAGE_ADDR\n"));
672				return ECHOSTATUS_DSP_DEAD;
673			}
674
675			dwReturn = Write_DSP( m_dwCommPagePhys );
676			if ( dwReturn != 0 )
677			{
678				ECHO_DEBUGPRINTF(("LoadDSP - Failed to write comm page address\n"));
679				return ECHOSTATUS_DSP_DEAD;
680			}
681
682			//
683			// Get the serial number via slave mode.
684			// This is triggered by the SET_COMMPAGE_ADDR command.
685			//	We don't actually use the serial number but we have to get
686			//	it as part of the DSP init vodoo.
687			//
688			Status = ReadSn();
689			if ( ECHOSTATUS_OK != Status )
690			{
691				ECHO_DEBUGPRINTF(("LoadDSP - Failed to read serial number\n"));
692				return Status;
693			}
694
695			m_pwDspCode = pCode;			// Show which DSP code loaded
696			m_bBadBoard = FALSE;			// DSP OK
697
698			ECHO_DEBUGPRINTF(("CDspCommObject::LoadDSP  Set m_bBadBoard to FALSE\n"));
699
700			return ECHOSTATUS_OK;
701		}
702
703		m_pOsSupport->OsGetSystemTime( &ullCurTime );
704	}
705
706	ECHO_DEBUGPRINTF( ("LoadDSP: DSP load timed out waiting for HF4\n") );
707
708	return ECHOSTATUS_DSP_TIMEOUT;
709
710}	// DWORD	CDspCommObject::LoadDSP
711
712
713
714
715//===========================================================================
716//
717// LoadFirmware takes care of loading the DSP and any ASIC code.
718//
719//===========================================================================
720
721ECHOSTATUS CDspCommObject::LoadFirmware()
722{
723	ECHOSTATUS	dwReturn;
724	ULONGLONG	ullRightNow;
725
726	// Sanity check
727	if ( NULL == m_pwDspCodeToLoad || NULL == m_pDspCommPage )
728	{
729		ECHO_DEBUGBREAK();
730		return ECHOSTATUS_NO_MEM;
731	}
732
733	//
734	// Even if the external box is off, an application may still try
735	// to repeatedly open the driver, causing multiple load attempts and
736	// making the machine spend lots of time in the kernel.  If the ASIC is not
737	// loaded, this code will gate the loading attempts so it doesn't happen
738	// more than once per second.
739	//
740	m_pOsSupport->OsGetSystemTime(&ullRightNow);
741	if ( 	(FALSE == m_bASICLoaded) &&
742			(DSP_LOAD_ATTEMPT_PERIOD > (ullRightNow - m_ullLastLoadAttemptTime)) )
743		return ECHOSTATUS_ASIC_NOT_LOADED;
744
745	//
746	// Update the timestamp
747	//
748	m_ullLastLoadAttemptTime = ullRightNow;
749
750	//
751	// See if the ASIC is present and working - only if the DSP is already loaded
752	//
753	if (NULL != m_pwDspCode)
754	{
755		dwReturn = CheckAsicStatus();
756		if (TRUE == dwReturn)
757			return ECHOSTATUS_OK;
758
759		//
760		// ASIC check failed; force the DSP to reload
761		//
762		m_pwDspCode = NULL;
763	}
764
765	//
766	// Try and load the DSP
767	//
768	dwReturn = LoadDSP( m_pwDspCodeToLoad );
769	if ( 	(ECHOSTATUS_OK != dwReturn) &&
770			(ECHOSTATUS_FIRMWARE_LOADED != dwReturn) )
771	{
772		return dwReturn;
773	}
774
775	ECHO_DEBUGPRINTF(("DSP load OK\n"));
776
777	//
778	// Load the ASIC if the DSP load succeeded; LoadASIC will
779	// always return TRUE for cards that don't have an ASIC.
780	//
781	dwReturn = LoadASIC();
782	if ( FALSE == dwReturn )
783	{
784		dwReturn = ECHOSTATUS_ASIC_NOT_LOADED;
785	}
786	else
787	{
788		//
789		// ASIC load was successful
790		//
791		RestoreDspSettings();
792
793		dwReturn = ECHOSTATUS_OK;
794	}
795
796	return dwReturn;
797
798}	// BOOL CDspCommObject::LoadFirmware()
799
800
801//===========================================================================
802//
803// This function is used to read back the serial number from the DSP;
804// this is triggered by the SET_COMMPAGE_ADDR command.
805//
806// Only some early Echogals products have serial numbers in the ROM;
807// the serial number is not used, but you still need to do this as
808// part of the DSP load process.
809//
810//===========================================================================
811
812ECHOSTATUS CDspCommObject::ReadSn()
813{
814	INT32			j;
815	DWORD			dwSn[ 6 ];
816	ECHOSTATUS	Status;
817
818	ECHO_DEBUGPRINTF( ("CDspCommObject::ReadSn\n") );
819	for ( j = 0; j < 5; j++ )
820	{
821		Status = Read_DSP( &dwSn[ j ] );
822		if ( Status != 0 )
823		{
824			ECHO_DEBUGPRINTF( ("\tFailed to read serial number word %ld\n",
825									 j) );
826			return ECHOSTATUS_DSP_DEAD;
827		}
828	}
829	ECHO_DEBUGPRINTF( ("\tRead serial number %08lx %08lx %08lx %08lx %08lx\n",
830							 dwSn[0], dwSn[1], dwSn[2], dwSn[3], dwSn[4]) );
831	return ECHOSTATUS_OK;
832
833}	// DWORD	CDspCommObject::ReadSn
834
835
836
837
838//===========================================================================
839//
840//	This is called after LoadFirmware to restore old gains, meters on,
841// monitors, etc.
842//
843//===========================================================================
844
845void CDspCommObject::RestoreDspSettings()
846{
847	ECHO_DEBUGPRINTF(("RestoreDspSettings\n"));
848	ECHO_DEBUGPRINTF(("\tControl reg is 0x%lx\n",SWAP(m_pDspCommPage->dwControlReg) ));
849
850	if ( !CheckAsicStatus() )
851		return;
852
853	m_pDspCommPage->dwHandshake = 0xffffffff;
854
855#ifdef MIDI_SUPPORT
856	m_ullNextMidiWriteTime = 0;
857#endif
858
859	SetSampleRate();
860	if ( 0 != m_wMeterOnCount )
861	{
862		SendVector( DSP_VC_METERS_ON );
863	}
864
865	SetInputClock( m_wInputClock );
866	SetOutputClock( m_wOutputClock );
867
868	if ( !WaitForHandshake() )
869	{
870		return;
871	}
872	UpdateAudioOutLineLevel();
873
874	if ( !WaitForHandshake() )
875		return;
876	UpdateAudioInLineLevel();
877
878	if ( HasVmixer() )
879	{
880		if ( !WaitForHandshake() )
881			return;
882		UpdateVmixerLevel();
883	}
884
885	if ( !WaitForHandshake() )
886		return;
887
888	ClearHandshake();
889	SendVector( DSP_VC_UPDATE_FLAGS );
890
891	ECHO_DEBUGPRINTF(("RestoreDspSettings done\n"));
892
893}	// void CDspCommObject::RestoreDspSettings()
894
895
896
897
898/****************************************************************************
899
900	DSP utilities
901
902 ****************************************************************************/
903
904//===========================================================================
905//
906// Write_DSP writes a 32-bit value to the DSP; this is used almost
907// exclusively for loading the DSP.
908//
909//===========================================================================
910
911ECHOSTATUS CDspCommObject::Write_DSP
912(
913	DWORD dwData				// 32 bit value to write to DSP data register
914)
915{
916	DWORD 		dwStatus;
917	ULONGLONG 	ullCurTime, ullTimeout;
918
919//	ECHO_DEBUGPRINTF(("Write_DSP\n"));
920
921	m_pOsSupport->OsGetSystemTime( &ullCurTime );
922	ullTimeout = ullCurTime + 10000000L;		// 10 sec.
923	while ( ullTimeout >= ullCurTime )
924	{
925		dwStatus = GetDspRegister( CHI32_STATUS_REG );
926		if ( ( dwStatus & CHI32_STATUS_HOST_WRITE_EMPTY ) != 0 )
927		{
928			SetDspRegister( CHI32_DATA_REG, dwData );
929//			ECHO_DEBUGPRINTF(("Write DSP: 0x%x", dwData));
930			return ECHOSTATUS_OK;
931		}
932		m_pOsSupport->OsGetSystemTime( &ullCurTime );
933	}
934
935	m_bBadBoard = TRUE;		// Set TRUE until DSP re-loaded
936
937	ECHO_DEBUGPRINTF(("CDspCommObject::Write_DSP  Set m_bBadBoard to TRUE\n"));
938
939	return ECHOSTATUS_DSP_TIMEOUT;
940
941}	// ECHOSTATUS CDspCommObject::Write_DSP
942
943
944
945
946//===========================================================================
947//
948// Read_DSP reads a 32-bit value from the DSP; this is used almost
949// exclusively for loading the DSP and checking the status of the ASIC.
950//
951//===========================================================================
952
953ECHOSTATUS CDspCommObject::Read_DSP
954(
955	DWORD *pdwData				// Ptr to 32 bit value read from DSP data register
956)
957{
958	DWORD 		dwStatus;
959	ULONGLONG	ullCurTime, ullTimeout;
960
961//	ECHO_DEBUGPRINTF(("Read_DSP\n"));
962	m_pOsSupport->OsGetSystemTime( &ullCurTime );
963
964	ullTimeout = ullCurTime + READ_DSP_TIMEOUT;
965	while ( ullTimeout >= ullCurTime )
966	{
967		dwStatus = GetDspRegister( CHI32_STATUS_REG );
968		if ( ( dwStatus & CHI32_STATUS_HOST_READ_FULL ) != 0 )
969		{
970			*pdwData = GetDspRegister( CHI32_DATA_REG );
971//			ECHO_DEBUGPRINTF(("Read DSP: 0x%x\n", *pdwData));
972			return ECHOSTATUS_OK;
973		}
974		m_pOsSupport->OsGetSystemTime( &ullCurTime );
975	}
976
977	m_bBadBoard = TRUE;		// Set TRUE until DSP re-loaded
978
979	ECHO_DEBUGPRINTF(("CDspCommObject::Read_DSP  Set m_bBadBoard to TRUE\n"));
980
981	return ECHOSTATUS_DSP_TIMEOUT;
982}	// ECHOSTATUS CDspCommObject::Read_DSP
983
984
985
986//===========================================================================
987//
988// Much of the interaction between the DSP and the driver is done via vector
989// commands; SendVector writes a vector command to the DSP.  Typically,
990// this causes the DSP to read or write fields in the comm page.
991//
992// Returns ECHOSTATUS_OK if sent OK.
993//
994//===========================================================================
995
996ECHOSTATUS CDspCommObject::SendVector
997(
998	DWORD dwCommand				// 32 bit command to send to DSP vector register
999)
1000{
1001	ULONGLONG	ullTimeout;
1002	ULONGLONG	ullCurTime;
1003
1004//
1005// Turn this on if you want to see debug prints for every vector command
1006//
1007#if 0
1008//#ifdef ECHO_DEBUG
1009	char *	pszCmd;
1010	switch ( dwCommand )
1011	{
1012		case DSP_VC_ACK_INT :
1013			pszCmd = "DSP_VC_ACK_INT";
1014			break;
1015		case DSP_VC_SET_VMIXER_GAIN :
1016			pszCmd = "DSP_VC_SET_VMIXER_GAIN";
1017			break;
1018		case DSP_VC_START_TRANSFER :
1019			pszCmd = "DSP_VC_START_TRANSFER";
1020			break;
1021		case DSP_VC_METERS_ON :
1022			pszCmd = "DSP_VC_METERS_ON";
1023			break;
1024		case DSP_VC_METERS_OFF :
1025			pszCmd = "DSP_VC_METERS_OFF";
1026			break;
1027		case DSP_VC_UPDATE_OUTVOL :
1028			pszCmd = "DSP_VC_UPDATE_OUTVOL";
1029			break;
1030		case DSP_VC_UPDATE_INGAIN :
1031			pszCmd = "DSP_VC_UPDATE_INGAIN";
1032			break;
1033		case DSP_VC_ADD_AUDIO_BUFFER :
1034			pszCmd = "DSP_VC_ADD_AUDIO_BUFFER";
1035			break;
1036		case DSP_VC_TEST_ASIC :
1037			pszCmd = "DSP_VC_TEST_ASIC";
1038			break;
1039		case DSP_VC_UPDATE_CLOCKS :
1040			pszCmd = "DSP_VC_UPDATE_CLOCKS";
1041			break;
1042		case DSP_VC_SET_LAYLA_SAMPLE_RATE :
1043			if ( GetCardType() == LAYLA )
1044				pszCmd = "DSP_VC_SET_LAYLA_RATE";
1045			else if ( GetCardType() == GINA || GetCardType() == DARLA )
1046				pszCmd = "DSP_VC_SET_GD_AUDIO_STATE";
1047			else
1048				pszCmd = "DSP_VC_WRITE_CONTROL_REG";
1049			break;
1050		case DSP_VC_MIDI_WRITE :
1051			pszCmd = "DSP_VC_MIDI_WRITE";
1052			break;
1053		case DSP_VC_STOP_TRANSFER :
1054			pszCmd = "DSP_VC_STOP_TRANSFER";
1055			break;
1056		case DSP_VC_UPDATE_FLAGS :
1057			pszCmd = "DSP_VC_UPDATE_FLAGS";
1058			break;
1059		case DSP_VC_RESET :
1060			pszCmd = "DSP_VC_RESET";
1061			break;
1062		default :
1063			pszCmd = "?????";
1064			break;
1065	}
1066
1067	ECHO_DEBUGPRINTF( ("SendVector: %s dwCommand %s (0x%x)\n",
1068								GetCardName(),
1069								pszCmd,
1070								dwCommand) );
1071#endif
1072
1073	m_pOsSupport->OsGetSystemTime( &ullCurTime );
1074	ullTimeout = ullCurTime + 100000L;		// 100m.s.
1075
1076	//
1077	// Wait for the "vector busy" bit to be off
1078	//
1079	while ( ullCurTime <= ullTimeout)
1080	{
1081		DWORD dwReg;
1082
1083		dwReg = GetDspRegister( CHI32_VECTOR_REG );
1084		if ( 0 == (dwReg & CHI32_VECTOR_BUSY) )
1085		{
1086			SetDspRegister( CHI32_VECTOR_REG, dwCommand );
1087
1088			return ECHOSTATUS_OK;
1089		}
1090		m_pOsSupport->OsGetSystemTime( &ullCurTime );
1091	}
1092
1093	ECHO_DEBUGPRINTF( ("\tPunked out on SendVector\n") );
1094	ECHO_DEBUGBREAK();
1095	return ECHOSTATUS_DSP_TIMEOUT;
1096
1097}	// ECHOSTATUS CDspCommObject::SendVector
1098
1099
1100
1101//===========================================================================
1102//
1103//	Some vector commands involve the DSP reading or writing data to and
1104// from the comm page; if you send one of these commands to the DSP,
1105// it will complete the command and then write a non-zero value to
1106// the dwHandshake field in the comm page.  This function waits for the
1107// handshake to show up.
1108//
1109//===========================================================================
1110
1111BOOL CDspCommObject::WaitForHandshake()
1112{
1113	ULONGLONG ullDelta;
1114	ULONGLONG ullStartTime,ullTime;
1115
1116	//
1117	// Wait up to three milliseconds for the handshake from the DSP
1118	//
1119	m_pOsSupport->OsGetSystemTime( &ullStartTime );
1120	do
1121	{
1122		// Look for the handshake value
1123		if ( 0 != GetHandshakeFlag() )
1124		{
1125			return TRUE;
1126		}
1127
1128		// Give the DSP time to access the comm page
1129		m_pOsSupport->OsSnooze( 2 );
1130
1131		m_pOsSupport->OsGetSystemTime(&ullTime);
1132		ullDelta = ullTime - ullStartTime;
1133	} while (ullDelta < (ULONGLONG) HANDSHAKE_TIMEOUT);
1134
1135	ECHO_DEBUGPRINTF( ("CDspCommObject::WaitForHandshake: Timeout waiting "
1136								"for DSP\n") );
1137	ECHO_DEBUGBREAK();
1138	return FALSE;
1139
1140}		// DWORD	CDspCommObject::WaitForHandshake()
1141
1142
1143
1144
1145/****************************************************************************
1146
1147	Transport methods
1148
1149 ****************************************************************************/
1150
1151//===========================================================================
1152//
1153// StartTransport starts transport for a set of pipes
1154//
1155//===========================================================================
1156
1157ECHOSTATUS CDspCommObject::StartTransport
1158(
1159	PCChannelMask	pChannelMask			// Pipes to start
1160)
1161{
1162	ECHO_DEBUGPRINTF( ("StartTransport\n") );
1163
1164	//
1165	// Wait for the previous command to complete
1166	//
1167	if ( !WaitForHandshake() )
1168		return ECHOSTATUS_DSP_DEAD;
1169
1170	//
1171	// Write the appropriate fields in the comm page
1172	//
1173	m_pDspCommPage->cmdStart.Clear();
1174	m_pDspCommPage->cmdStart = *pChannelMask;
1175	if ( !m_pDspCommPage->cmdStart.IsEmpty() )
1176	{
1177		//
1178		// Clear the handshake and send the vector command
1179		//
1180		ClearHandshake();
1181		SendVector( DSP_VC_START_TRANSFER );
1182
1183		//
1184		// Keep track of which pipes are transporting
1185		//
1186		m_cmActive += *pChannelMask;
1187
1188		return ECHOSTATUS_OK;
1189	}		// if this monkey is being started
1190
1191	ECHO_DEBUGPRINTF( ("CDspCommObject::StartTransport: No pipes to start!\n") );
1192	return ECHOSTATUS_INVALID_CHANNEL;
1193
1194}	// ECHOSTATUS CDspCommObject::StartTransport
1195
1196
1197//===========================================================================
1198//
1199// StopTransport pauses transport for a set of pipes
1200//
1201//===========================================================================
1202
1203ECHOSTATUS CDspCommObject::StopTransport
1204(
1205	PCChannelMask	pChannelMask
1206)
1207{
1208	ECHO_DEBUGPRINTF(("StopTransport\n"));
1209
1210	//
1211	// Wait for the last command to finish
1212	//
1213	if ( !WaitForHandshake() )
1214		return ECHOSTATUS_DSP_DEAD;
1215
1216	//
1217	// Write to the comm page
1218	//
1219	m_pDspCommPage->cmdStop.Clear();
1220	m_pDspCommPage->cmdStop = *pChannelMask;
1221	m_pDspCommPage->cmdReset.Clear();
1222	if ( !m_pDspCommPage->cmdStop.IsEmpty() )
1223	{
1224		//
1225		// Clear the handshake and send the vector command
1226		//
1227		ClearHandshake();
1228		SendVector( DSP_VC_STOP_TRANSFER );
1229
1230		//
1231		// Keep track of which pipes are transporting
1232		//
1233		m_cmActive -= *pChannelMask;
1234
1235		return ECHOSTATUS_OK;
1236	}		// if this monkey is being started
1237
1238	ECHO_DEBUGPRINTF( ("CDspCommObject::StopTransport: No pipes to stop!\n") );
1239	return ECHOSTATUS_OK;
1240
1241}	// ECHOSTATUS CDspCommObject::StopTransport
1242
1243
1244//===========================================================================
1245//
1246// ResetTransport resets transport for a set of pipes
1247//
1248//===========================================================================
1249
1250ECHOSTATUS CDspCommObject::ResetTransport
1251(
1252	PCChannelMask	pChannelMask
1253)
1254{
1255	ECHO_DEBUGPRINTF(("ResetTransport\n"));
1256
1257	//
1258	// Wait for the last command to finish
1259	//
1260	if ( !WaitForHandshake() )
1261		return ECHOSTATUS_DSP_DEAD;
1262
1263	//
1264	// Write to the comm page
1265	//
1266	m_pDspCommPage->cmdStop.Clear();
1267	m_pDspCommPage->cmdReset.Clear();
1268	m_pDspCommPage->cmdStop = *pChannelMask;
1269	m_pDspCommPage->cmdReset = *pChannelMask;
1270	if ( !m_pDspCommPage->cmdReset.IsEmpty() )
1271	{
1272		//
1273		// Clear the handshake and send the vector command
1274		//
1275		ClearHandshake();
1276		SendVector( DSP_VC_STOP_TRANSFER );
1277
1278		//
1279		// Keep track of which pipes are transporting
1280		//
1281		m_cmActive -= *pChannelMask;
1282
1283		return ECHOSTATUS_OK;
1284	}		// if this monkey is being started
1285
1286	ECHO_DEBUGPRINTF( ("CDspCommObject::ResetTransport: No pipes to reset!\n") );
1287	return ECHOSTATUS_OK;
1288
1289}	// ECHOSTATUS CDspCommObject::ResetTransport
1290
1291
1292//===========================================================================
1293//
1294// This tells the DSP where to start reading the scatter-gather list
1295// for a given pipe.
1296//
1297//===========================================================================
1298
1299void CDspCommObject::SetAudioDuckListPhys
1300(
1301	WORD	wPipeIndex,			// Pipe index
1302	DWORD dwNewPhysAdr		// Physical address asserted on the PCI bus
1303)
1304{
1305	if (wPipeIndex < GetNumPipes() )
1306	{
1307		m_pDspCommPage->DuckListPhys[ wPipeIndex ].PhysAddr =
1308																		SWAP( dwNewPhysAdr );
1309	}
1310}	// void CDspCommObject::SetAudioDuckListPhys
1311
1312
1313
1314//===========================================================================
1315//
1316// Get a mask with active pipes
1317//
1318//===========================================================================
1319
1320void CDspCommObject::GetActivePipes
1321(
1322	PCChannelMask	pChannelMask
1323)
1324{
1325	pChannelMask->Clear();
1326	*pChannelMask += m_cmActive;
1327}	// void CDspCommObject::GetActivePipes()
1328
1329
1330//===========================================================================
1331//
1332//	Set the audio format for a pipe
1333//
1334//===========================================================================
1335
1336ECHOSTATUS CDspCommObject::SetAudioFormat
1337(
1338	WORD 							wPipeIndex,
1339	PECHOGALS_AUDIOFORMAT	pFormat
1340)
1341{
1342	WORD wDspFormat = DSP_AUDIOFORM_SS_16LE;
1343
1344	ECHO_DEBUGPRINTF(("CDspCommObject::SetAudioFormat - pipe %d  bps %d  channels %d\n",
1345							wPipeIndex,pFormat->wBitsPerSample,pFormat->wDataInterleave));
1346
1347	//
1348	// Check the pipe number
1349	//
1350	if (wPipeIndex >= GetNumPipes() )
1351	{
1352		ECHO_DEBUGPRINTF( ("CDspCommObject::SetAudioFormat: Invalid pipe"
1353								 "%d\n",
1354								 wPipeIndex) );
1355		return ECHOSTATUS_INVALID_CHANNEL;
1356	}
1357
1358	//
1359	// Look for super-interleave
1360	//
1361	if (pFormat->wDataInterleave > 2)
1362	{
1363		switch (pFormat->wBitsPerSample)
1364		{
1365			case 16 :
1366				wDspFormat = DSP_AUDIOFORM_SUPER_INTERLEAVE_16LE;
1367				break;
1368
1369			case 24 :
1370				wDspFormat = DSP_AUDIOFORM_SUPER_INTERLEAVE_24LE;
1371				break;
1372
1373			case 32 :
1374				wDspFormat = DSP_AUDIOFORM_SUPER_INTERLEAVE_32LE;
1375				break;
1376		}
1377
1378		wDspFormat |= pFormat->wDataInterleave;
1379	}
1380	else
1381	{
1382		//
1383		// For big-endian data, only 32 bit mono->mono samples and 32 bit stereo->stereo
1384		// are supported
1385		//
1386		if (pFormat->byDataAreBigEndian)
1387		{
1388
1389			switch ( pFormat->wDataInterleave )
1390			{
1391				case 1 :
1392					wDspFormat = DSP_AUDIOFORM_MM_32BE;
1393					break;
1394
1395#ifdef STEREO_BIG_ENDIAN32_SUPPORT
1396				case 2 :
1397					wDspFormat = DSP_AUDIOFORM_SS_32BE;
1398					break;
1399#endif
1400
1401			}
1402		}
1403		else
1404		{
1405			//
1406			// Check for 32 bit little-endian mono->mono case
1407			//
1408			if ( 	(1 == pFormat->wDataInterleave) &&
1409					(32 == pFormat->wBitsPerSample) &&
1410					(0 == pFormat->byMonoToStereo) )
1411			{
1412				wDspFormat = DSP_AUDIOFORM_MM_32LE;
1413			}
1414			else
1415			{
1416				//
1417				// Handle the other little-endian formats
1418				//
1419				switch (pFormat->wBitsPerSample)
1420				{
1421					case 8 :
1422						if (2 == pFormat->wDataInterleave)
1423							wDspFormat = DSP_AUDIOFORM_SS_8;
1424						else
1425							wDspFormat = DSP_AUDIOFORM_MS_8;
1426
1427						break;
1428
1429					default :
1430					case 16 :
1431						if (2 == pFormat->wDataInterleave)
1432							wDspFormat = DSP_AUDIOFORM_SS_16LE;
1433						else
1434							wDspFormat = DSP_AUDIOFORM_MS_16LE;
1435						break;
1436
1437					case 24 :
1438						if (2 == pFormat->wDataInterleave)
1439							wDspFormat = DSP_AUDIOFORM_SS_24LE;
1440						else
1441							wDspFormat = DSP_AUDIOFORM_MS_24LE;
1442						break;
1443
1444					case 32 :
1445						if (2 == pFormat->wDataInterleave)
1446							wDspFormat = DSP_AUDIOFORM_SS_32LE;
1447						else
1448							wDspFormat = DSP_AUDIOFORM_MS_32LE;
1449						break;
1450				}
1451
1452			} // check other little-endian formats
1453
1454		} // not big endian data
1455
1456	} // not super-interleave
1457
1458	m_pDspCommPage->wAudioFormat[wPipeIndex] = SWAP( wDspFormat );
1459
1460	return ECHOSTATUS_OK;
1461
1462}	// ECHOSTATUS CDspCommObject::SetAudioFormat
1463
1464
1465//===========================================================================
1466//
1467//	Get the audio format for a pipe
1468//
1469//===========================================================================
1470
1471ECHOSTATUS CDspCommObject::GetAudioFormat
1472(
1473	WORD 							wPipeIndex,
1474	PECHOGALS_AUDIOFORMAT	pFormat
1475)
1476{
1477	if (wPipeIndex >= GetNumPipes() )
1478	{
1479		ECHO_DEBUGPRINTF( ("CDspCommObject::GetAudioFormat: Invalid pipe %d\n",
1480								 wPipeIndex) );
1481
1482		return ECHOSTATUS_INVALID_CHANNEL;
1483	}
1484
1485	pFormat->byDataAreBigEndian = 0;	// true for most of the formats
1486	pFormat->byMonoToStereo = 0;
1487
1488	switch (SWAP(m_pDspCommPage->wAudioFormat[wPipeIndex]))
1489	{
1490		case DSP_AUDIOFORM_MS_8 :
1491			pFormat->wDataInterleave = 1;
1492			pFormat->wBitsPerSample = 8;
1493			pFormat->byMonoToStereo = 1;
1494			break;
1495
1496		case DSP_AUDIOFORM_MS_16LE :
1497			pFormat->wDataInterleave = 1;
1498			pFormat->wBitsPerSample = 16;
1499			pFormat->byMonoToStereo = 1;
1500			break;
1501
1502		case DSP_AUDIOFORM_SS_8 :
1503			pFormat->wDataInterleave = 2;
1504			pFormat->wBitsPerSample = 8;
1505			break;
1506
1507		case DSP_AUDIOFORM_SS_16LE :
1508			pFormat->wDataInterleave = 2;
1509			pFormat->wBitsPerSample = 16;
1510			break;
1511
1512		case DSP_AUDIOFORM_SS_32LE :
1513			pFormat->wDataInterleave = 2;
1514			pFormat->wBitsPerSample = 32;
1515			break;
1516
1517		case DSP_AUDIOFORM_MS_32LE :
1518			pFormat->byMonoToStereo = 1;
1519			// fall through
1520
1521		case DSP_AUDIOFORM_MM_32LE :
1522			pFormat->wDataInterleave = 1;
1523			pFormat->wBitsPerSample = 32;
1524			break;
1525
1526		case DSP_AUDIOFORM_MM_32BE :
1527			pFormat->wDataInterleave = 1;
1528			pFormat->wBitsPerSample = 32;
1529			pFormat->byDataAreBigEndian = 1;
1530			break;
1531
1532		case DSP_AUDIOFORM_SS_32BE :
1533			pFormat->wDataInterleave = 2;
1534			pFormat->wBitsPerSample = 32;
1535			pFormat->byDataAreBigEndian = 1;
1536			break;
1537
1538	}
1539
1540	return ECHOSTATUS_OK;
1541
1542}	// void CDspCommObject::GetAudioFormat
1543
1544
1545
1546/****************************************************************************
1547
1548	Mixer methods
1549
1550 ****************************************************************************/
1551
1552//===========================================================================
1553//
1554// SetPipeOutGain - set the gain for a single output pipe
1555//
1556//===========================================================================
1557
1558ECHOSTATUS CDspCommObject::SetPipeOutGain
1559(
1560	WORD 	wPipeOut,
1561	WORD	wBusOut,
1562	INT32	iGain,
1563	BOOL 	fImmediate
1564)
1565{
1566	if ( wPipeOut < m_wNumPipesOut )
1567	{
1568		//
1569		// Wait for the handshake
1570		//
1571		if ( !WaitForHandshake() )
1572			return ECHOSTATUS_DSP_DEAD;
1573
1574		//
1575		// Save the new value
1576		//
1577		iGain = GENERIC_TO_DSP(iGain);
1578		m_pDspCommPage->OutLineLevel[ wPipeOut ] = (BYTE) iGain;
1579
1580		/*
1581		ECHO_DEBUGPRINTF( ("CDspCommObject::SetPipeOutGain: Out pipe %d "
1582								 "= 0x%lx\n",
1583								 wPipeOut,
1584								 iGain) );
1585		*/
1586
1587		//
1588		// If fImmediate is true, then do the gain setting right now.
1589		// If you want to do a batch of gain settings all at once, it's
1590		// more efficient to call this several times and then only set
1591		// fImmediate for the last one; then the DSP picks up all of
1592		// them at once.
1593		//
1594		if (fImmediate)
1595		{
1596			return UpdateAudioOutLineLevel();
1597		}
1598
1599		return ECHOSTATUS_OK;
1600
1601	}
1602
1603	ECHO_DEBUGPRINTF( ("CDspCommObject::SetPipeOutGain: Invalid out pipe "
1604							 "%d\n",
1605							 wPipeOut) );
1606	ECHO_DEBUGBREAK();
1607
1608	return ECHOSTATUS_INVALID_CHANNEL;
1609
1610}	// SetPipeOutGain
1611
1612
1613//===========================================================================
1614//
1615// GetPipeOutGain returns the current gain for an output pipe.  This isn't
1616// really used as the mixer code in CEchoGals stores logical values for
1617// these, but it's here for completeness.
1618//
1619//===========================================================================
1620
1621ECHOSTATUS CDspCommObject::GetPipeOutGain
1622(
1623	WORD 	wPipeOut,
1624	WORD 	wBusOut,
1625	INT32 &iGain
1626)
1627{
1628	if (wPipeOut < m_wNumPipesOut)
1629	{
1630		iGain = (INT32) (char) m_pDspCommPage->OutLineLevel[ wPipeOut ];
1631		iGain = DSP_TO_GENERIC(8);
1632		return ECHOSTATUS_OK;
1633	}
1634
1635	ECHO_DEBUGPRINTF( ("CDspCommObject::GetPipeOutGain: Invalid out pipe "
1636							 "%d\n",
1637							 wPipeOut) );
1638
1639	return ECHOSTATUS_INVALID_CHANNEL;
1640
1641}	// GetPipeOutGain
1642
1643
1644
1645//===========================================================================
1646//
1647// Set input bus gain - iGain is in units of 0.5 dB
1648//
1649//===========================================================================
1650
1651ECHOSTATUS CDspCommObject::SetBusInGain( WORD wBusIn, INT32 iGain)
1652{
1653	if (wBusIn > m_wNumBussesIn)
1654		return ECHOSTATUS_INVALID_CHANNEL;
1655
1656	//
1657	// Wait for the handshake (OK even if ASIC is not loaded)
1658	//
1659	if ( !WaitForHandshake() )
1660		return ECHOSTATUS_DSP_DEAD;
1661
1662	//
1663	// Adjust the gain value
1664	//
1665	iGain += GL20_INPUT_GAIN_MAGIC_NUMBER;
1666
1667	//
1668	// Put it in the comm page
1669	//
1670	m_pDspCommPage->InLineLevel[wBusIn] = (BYTE) iGain;
1671
1672	return UpdateAudioInLineLevel();
1673}
1674
1675
1676//===========================================================================
1677//
1678// Get the input bus gain in units of 0.5 dB
1679//
1680//===========================================================================
1681
1682ECHOSTATUS CDspCommObject::GetBusInGain( WORD wBusIn, INT32 &iGain)
1683{
1684	if (wBusIn > m_wNumBussesIn)
1685		return ECHOSTATUS_INVALID_CHANNEL;
1686
1687	iGain = m_pDspCommPage->InLineLevel[wBusIn];
1688	iGain -= GL20_INPUT_GAIN_MAGIC_NUMBER;
1689
1690	return ECHOSTATUS_OK;
1691}
1692
1693
1694//===========================================================================
1695//
1696//	Set the nominal level for an input or output bus
1697//
1698// bState TRUE			-10 nominal level
1699// bState FALSE		+4 nominal level
1700//
1701//===========================================================================
1702
1703ECHOSTATUS CDspCommObject::SetNominalLevel
1704(
1705	WORD	wBus,
1706	BOOL	bState
1707)
1708{
1709	//
1710	// Check the pipe index
1711	//
1712	if (wBus < (m_wNumBussesOut + m_wNumBussesIn))
1713	{
1714		//
1715		// Wait for the handshake (OK even if ASIC is not loaded)
1716		//
1717		if ( !WaitForHandshake() )
1718			return ECHOSTATUS_DSP_DEAD;
1719
1720		//
1721		// Set the nominal bit
1722		//
1723		if ( bState )
1724			m_pDspCommPage->cmdNominalLevel.SetIndexInMask( wBus );
1725		else
1726			m_pDspCommPage->cmdNominalLevel.ClearIndexInMask( wBus );
1727
1728		return UpdateAudioOutLineLevel();
1729	}
1730
1731	ECHO_DEBUGPRINTF( ("CDspCommObject::SetNominalOutLineLevel Invalid "
1732							 "index %d\n",
1733							 wBus ) );
1734	return ECHOSTATUS_INVALID_CHANNEL;
1735
1736}	// ECHOSTATUS CDspCommObject::SetNominalLevel
1737
1738
1739//===========================================================================
1740//
1741//	Get the nominal level for an input or output bus
1742//
1743// bState TRUE			-10 nominal level
1744// bState FALSE		+4 nominal level
1745//
1746//===========================================================================
1747
1748ECHOSTATUS CDspCommObject::GetNominalLevel
1749(
1750	WORD	wBus,
1751	PBYTE pbyState
1752)
1753{
1754
1755	if (wBus < (m_wNumBussesOut + m_wNumBussesIn))
1756	{
1757		*pbyState = (BYTE)
1758			m_pDspCommPage->cmdNominalLevel.TestIndexInMask( wBus );
1759		return ECHOSTATUS_OK;
1760	}
1761
1762	ECHO_DEBUGPRINTF( ("CDspCommObject::GetNominalLevel Invalid "
1763							 "index %d\n",
1764							 wBus ) );
1765	return ECHOSTATUS_INVALID_CHANNEL;
1766}	// ECHOSTATUS CDspCommObject::GetNominalLevel
1767
1768
1769//===========================================================================
1770//
1771//	Set the monitor level from an input bus to an output bus.
1772//
1773//===========================================================================
1774
1775ECHOSTATUS CDspCommObject::SetAudioMonitor
1776(
1777	WORD	wBusOut,	// output bus
1778	WORD	wBusIn,	// input bus
1779	INT32	iGain,
1780	BOOL 	fImmediate
1781)
1782{
1783	/*
1784	ECHO_DEBUGPRINTF( ("CDspCommObject::SetAudioMonitor: "
1785							 "Out %d in %d Gain %d (0x%x)\n",
1786							 wBusOut, wBusIn, iGain, iGain) );
1787	*/
1788
1789	//
1790	// The monitor array is a one-dimensional array;
1791	// compute the offset into the array
1792	//
1793	WORD	wOffset = ComputeAudioMonitorIndex( wBusOut, wBusIn );
1794
1795	//
1796	// Wait for the offset
1797	//
1798	if ( !WaitForHandshake() )
1799		return ECHOSTATUS_DSP_DEAD;
1800
1801	//
1802	// Write the gain value to the comm page
1803	//
1804	iGain = GENERIC_TO_DSP(iGain);
1805	m_pDspCommPage->byMonitors[ wOffset ] = (BYTE) (iGain);
1806
1807	//
1808	// If fImmediate is set, do the command right now
1809	//
1810	if (fImmediate)
1811	{
1812		return UpdateAudioOutLineLevel();
1813	}
1814
1815	return ECHOSTATUS_OK;
1816
1817}	// ECHOSTATUS CDspCommObject::SetAudioMonitor
1818
1819
1820//===========================================================================
1821//
1822// SetMetersOn turns the meters on or off.  If meters are turned on, the
1823// DSP will write the meter and clock detect values to the comm page
1824// at about 30 Hz.
1825//
1826//===========================================================================
1827
1828ECHOSTATUS CDspCommObject::SetMetersOn
1829(
1830	BOOL bOn
1831)
1832{
1833	if ( bOn )
1834	{
1835		if ( 0 == m_wMeterOnCount )
1836		{
1837			SendVector( DSP_VC_METERS_ON );
1838		}
1839		m_wMeterOnCount++;
1840	}
1841	else
1842	{
1843		INT32	iDevice;
1844
1845		if ( m_wMeterOnCount == 0 )
1846			return ECHOSTATUS_OK;
1847
1848		if ( 0 == --m_wMeterOnCount )
1849		{
1850			SendVector( DSP_VC_METERS_OFF );
1851
1852			for ( iDevice = 0; iDevice < DSP_MAXPIPES; iDevice++ )
1853			{
1854				BYTE muted;
1855
1856				muted = (BYTE) GENERIC_TO_DSP(ECHOGAIN_MUTED);
1857				m_pDspCommPage->VUMeter[ iDevice ]   = muted;
1858				m_pDspCommPage->PeakMeter[ iDevice ] = muted;
1859			}
1860		}
1861	}
1862	return ECHOSTATUS_OK;
1863
1864}	// ECHOSTATUS CDspCommObject::SetMetersOn
1865
1866
1867//===========================================================================
1868//
1869// Tell the DSP to read and update output, nominal & monitor levels
1870//	in comm page.
1871//
1872//===========================================================================
1873
1874ECHOSTATUS CDspCommObject::UpdateAudioOutLineLevel()
1875{
1876	//ECHO_DEBUGPRINTF( ( "CDspCommObject::UpdateAudioOutLineLevel:\n" ) );
1877
1878	if (FALSE == m_bASICLoaded)
1879		return ECHOSTATUS_ASIC_NOT_LOADED;
1880
1881	ClearHandshake();
1882	return( SendVector( DSP_VC_UPDATE_OUTVOL ) );
1883
1884}	// ECHOSTATUS CDspCommObject::UpdateAudioOutLineLevel()
1885
1886
1887//===========================================================================
1888//
1889// Tell the DSP to read and update input levels in comm page
1890//
1891//===========================================================================
1892
1893ECHOSTATUS CDspCommObject::UpdateAudioInLineLevel()
1894{
1895	//ECHO_DEBUGPRINTF( ( "CDspCommObject::UpdateAudioInLineLevel:\n" ) );
1896
1897	if (FALSE == m_bASICLoaded)
1898		return ECHOSTATUS_ASIC_NOT_LOADED;
1899
1900	ClearHandshake();
1901	return( SendVector( DSP_VC_UPDATE_INGAIN ) );
1902}		// ECHOSTATUS CDspCommObject::UpdateAudioInLineLevel()
1903
1904
1905//===========================================================================
1906//
1907// Tell the DSP to read and update virtual mixer levels
1908//	in comm page.  This method is overridden by cards that actually
1909// support a vmixer.
1910//
1911//===========================================================================
1912
1913ECHOSTATUS CDspCommObject::UpdateVmixerLevel()
1914{
1915	ECHO_DEBUGPRINTF(("CDspCommObject::UpdateVmixerLevel\n"));
1916	return ECHOSTATUS_NOT_SUPPORTED;
1917}	// ECHOSTATUS CDspCommObject::UpdateVmixerLevel()
1918
1919
1920//===========================================================================
1921//
1922// Tell the DSP to change the input clock
1923//
1924//===========================================================================
1925
1926ECHOSTATUS CDspCommObject::SetInputClock(WORD wClock)
1927{
1928	//
1929	// Wait for the last command
1930	//
1931	if (!WaitForHandshake())
1932		return ECHOSTATUS_DSP_DEAD;
1933
1934	ECHO_DEBUGPRINTF( ("CDspCommObject::SetInputClock:\n") );
1935
1936	//
1937	// Write to the comm page
1938	//
1939	m_pDspCommPage->wInputClock = SWAP(wClock);
1940
1941	//
1942	// Clear the handshake and send the command
1943	//
1944	ClearHandshake();
1945	ECHOSTATUS Status = SendVector(DSP_VC_UPDATE_CLOCKS);
1946
1947	return Status;
1948
1949}	// ECHOSTATUS CDspCommObject::SetInputClock
1950
1951
1952//===========================================================================
1953//
1954// Tell the DSP to change the output clock - Layla20 only
1955//
1956//===========================================================================
1957
1958ECHOSTATUS CDspCommObject::SetOutputClock(WORD wClock)
1959{
1960
1961	return ECHOSTATUS_CLOCK_NOT_SUPPORTED;
1962
1963}	// ECHOSTATUS CDspCommObject::SetOutputClock
1964
1965
1966//===========================================================================
1967//
1968// Fill out an ECHOGALS_METERS struct using the current values in the
1969// comm page.  This method is overridden for vmixer cards.
1970//
1971//===========================================================================
1972
1973ECHOSTATUS CDspCommObject::GetAudioMeters
1974(
1975	PECHOGALS_METERS	pMeters
1976)
1977{
1978	pMeters->iNumPipesOut = 0;
1979	pMeters->iNumPipesIn = 0;
1980
1981	//
1982	//	Output
1983	//
1984	DWORD dwCh = 0;
1985	WORD 	i;
1986
1987	pMeters->iNumBussesOut = (INT32) m_wNumBussesOut;
1988	for (i = 0; i < m_wNumBussesOut; i++)
1989	{
1990		pMeters->iBusOutVU[i] =
1991			DSP_TO_GENERIC( ((INT32) (INT8) m_pDspCommPage->VUMeter[ dwCh ]) );
1992
1993		pMeters->iBusOutPeak[i] =
1994			DSP_TO_GENERIC( ((INT32) (INT8) m_pDspCommPage->PeakMeter[ dwCh ]) );
1995
1996		dwCh++;
1997	}
1998
1999	pMeters->iNumBussesIn = (INT32) m_wNumBussesIn;
2000	for (i = 0; i < m_wNumBussesIn; i++)
2001	{
2002		pMeters->iBusInVU[i] =
2003			DSP_TO_GENERIC( ((INT32) (INT8) m_pDspCommPage->VUMeter[ dwCh ]) );
2004		pMeters->iBusInPeak[i] =
2005			DSP_TO_GENERIC( ((INT32) (INT8) m_pDspCommPage->PeakMeter[ dwCh ]) );
2006
2007		dwCh++;
2008	}
2009
2010	return ECHOSTATUS_OK;
2011
2012} // GetAudioMeters
2013
2014
2015#ifdef DIGITAL_INPUT_AUTO_MUTE_SUPPORT
2016
2017//===========================================================================
2018//
2019// Digital input auto-mute - Gina24, Layla24, and Mona only
2020//
2021//===========================================================================
2022
2023ECHOSTATUS CDspCommObject::GetDigitalInputAutoMute(BOOL &fAutoMute)
2024{
2025	fAutoMute = m_fDigitalInAutoMute;
2026
2027	ECHO_DEBUGPRINTF(("CDspCommObject::GetDigitalInputAutoMute %d\n",fAutoMute));
2028
2029	return ECHOSTATUS_OK;
2030}
2031
2032ECHOSTATUS CDspCommObject::SetDigitalInputAutoMute(BOOL fAutoMute)
2033{
2034	ECHO_DEBUGPRINTF(("CDspCommObject::SetDigitalInputAutoMute %d\n",fAutoMute));
2035
2036	//
2037	// Store the flag
2038	//
2039	m_fDigitalInAutoMute = fAutoMute;
2040
2041	//
2042	// Re-set the input clock to the current value - indirectly causes the
2043	// auto-mute flag to be sent to the DSP
2044	//
2045	SetInputClock(m_wInputClock);
2046
2047	return ECHOSTATUS_OK;
2048}
2049
2050#endif // DIGITAL_INPUT_AUTO_MUTE_SUPPORT
2051
2052
2053
2054
2055/****************************************************************************
2056
2057	Power management
2058
2059 ****************************************************************************/
2060
2061//===========================================================================
2062//
2063// Tell the DSP to go into low-power mode
2064//
2065//===========================================================================
2066
2067ECHOSTATUS CDspCommObject::GoComatose()
2068{
2069	ECHO_DEBUGPRINTF(("CDspCommObject::GoComatose\n"));
2070
2071	if (NULL != m_pwDspCode)
2072	{
2073		//
2074		// Make LoadFirmware do a complete reload
2075		//
2076		m_pwDspCode = NULL;
2077
2078		//
2079		// Make sure that the sample rate get re-set on wakeup
2080		// (really only for Indigo and Mia)
2081		//
2082		m_pDspCommPage->dwControlReg = 0;
2083
2084		//
2085		// Put the DSP to sleep
2086		//
2087		return SendVector(DSP_VC_GO_COMATOSE);
2088	}
2089
2090	return ECHOSTATUS_OK;
2091
2092}	// end of GoComatose
2093
2094
2095
2096#ifdef MIDI_SUPPORT
2097
2098/****************************************************************************
2099
2100	MIDI
2101
2102 ****************************************************************************/
2103
2104//===========================================================================
2105//
2106// Send a buffer full of MIDI data to the DSP
2107//
2108//===========================================================================
2109
2110ECHOSTATUS CDspCommObject::WriteMidi
2111(
2112	PBYTE		pData,						// Ptr to data buffer
2113	DWORD		dwLength,					// How many bytes to write
2114	PDWORD	pdwActualCt					// Return how many actually written
2115)
2116{
2117	DWORD 		dwWriteCount,dwHandshake,dwStatus;
2118	BYTE			*pOutBuffer;
2119
2120
2121	//
2122	// Return immediately if the handshake flag is clar
2123	//
2124	dwHandshake = GetHandshakeFlag();
2125	if (0 == dwHandshake)
2126	{
2127		ECHO_DEBUGPRINTF(("CDCO::WriteMidi - can't write - handshake %ld\n",dwHandshake));
2128
2129		*pdwActualCt = 0;
2130		return ECHOSTATUS_BUSY;
2131	}
2132
2133	//
2134	// Return immediately if HF4 is clear - HF4 indicates that it is safe
2135	// to write MIDI output data
2136	//
2137	dwStatus = GetDspRegister( CHI32_STATUS_REG );
2138	if ( 0 == (dwStatus & CHI32_STATUS_REG_HF4 ) )
2139	{
2140		ECHO_DEBUGPRINTF(("CDCO::WriteMidi - can't write - dwStatus 0x%lx\n",dwStatus));
2141
2142		*pdwActualCt = 0;
2143		return ECHOSTATUS_BUSY;
2144	}
2145
2146
2147	//
2148	// Copy data to the comm page; limit to the amount of space in the DSP output
2149	// FIFO and in the comm page
2150	//
2151	dwWriteCount = dwLength;
2152	if (dwWriteCount > (CP_MIDI_OUT_BUFFER_SIZE - 1))
2153	{
2154		dwWriteCount = CP_MIDI_OUT_BUFFER_SIZE - 1;
2155	}
2156
2157	ECHO_DEBUGPRINTF(("WriteMidi - dwWriteCount %ld\n",dwWriteCount));
2158
2159	*pdwActualCt = dwWriteCount;	// Save the # of bytes written for the caller
2160
2161	pOutBuffer = m_pDspCommPage->byMidiOutData;
2162	*pOutBuffer = (BYTE) dwWriteCount;
2163
2164	pOutBuffer++;
2165
2166	OsCopyMemory(pOutBuffer,pData,dwWriteCount);
2167
2168	//
2169	// Send the command to the DSP
2170	//
2171	ClearHandshake();
2172	m_pDspCommPage->dwMidiOutFreeCount = 0;
2173	SendVector( DSP_VC_MIDI_WRITE );
2174
2175	//
2176	// Save the current time - used to detect if MIDI out is currently busy
2177	//
2178	ULONGLONG ullTime;
2179
2180	m_pOsSupport->OsGetSystemTime( &ullTime );
2181	m_ullMidiOutTime = ullTime;
2182
2183	return ECHOSTATUS_OK;
2184
2185}		// ECHOSTATUS CDspCommObject::WriteMidi
2186
2187
2188//===========================================================================
2189//
2190// Called from the interrupt handler - get a MIDI input byte
2191//
2192//===========================================================================
2193
2194ECHOSTATUS CDspCommObject::ReadMidi
2195(
2196	WORD 		wIndex,				// Buffer index
2197	DWORD &	dwData				// Return data
2198)
2199{
2200	if ( wIndex >= CP_MIDI_IN_BUFFER_SIZE )
2201		return ECHOSTATUS_INVALID_INDEX;
2202
2203	//
2204	// Get the data
2205	//
2206	dwData = SWAP( m_pDspCommPage->wMidiInData[ wIndex ] );
2207
2208	//
2209	// Timestamp for the MIDI input activity indicator
2210	//
2211	ULONGLONG ullTime;
2212
2213	m_pOsSupport->OsGetSystemTime( &ullTime );
2214	m_ullMidiInTime = ullTime;
2215
2216	return ECHOSTATUS_OK;
2217
2218}	// ECHOSTATUS CDspCommObject::ReadMidi
2219
2220
2221ECHOSTATUS CDspCommObject::SetMidiOn( BOOL bOn )
2222{
2223	if ( bOn )
2224	{
2225		if ( 0 == m_wMidiOnCount )
2226		{
2227			if ( !WaitForHandshake() )
2228				return ECHOSTATUS_DSP_DEAD;
2229
2230			m_pDspCommPage->dwFlags |= SWAP( (DWORD) DSP_FLAG_MIDI_INPUT );
2231
2232			ClearHandshake();
2233			SendVector( DSP_VC_UPDATE_FLAGS );
2234		}
2235		m_wMidiOnCount++;
2236	}
2237	else
2238	{
2239		if ( m_wMidiOnCount == 0 )
2240			return ECHOSTATUS_OK;
2241
2242		if ( 0 == --m_wMidiOnCount )
2243		{
2244			if ( !WaitForHandshake() )
2245				return ECHOSTATUS_DSP_DEAD;
2246
2247			m_pDspCommPage->dwFlags &= SWAP( (DWORD) ~DSP_FLAG_MIDI_INPUT );
2248
2249			ClearHandshake();
2250			SendVector( DSP_VC_UPDATE_FLAGS );
2251		}
2252	}
2253
2254	return ECHOSTATUS_OK;
2255
2256}	// ECHOSTATUS CDspCommObject::SetMidiOn
2257
2258
2259//===========================================================================
2260//
2261// Detect MIDI output activity
2262//
2263//===========================================================================
2264
2265BOOL CDspCommObject::IsMidiOutActive()
2266{
2267	ULONGLONG	ullCurTime;
2268
2269	m_pOsSupport->OsGetSystemTime( &ullCurTime );
2270	return( ( ( ullCurTime - m_ullMidiOutTime ) > MIDI_ACTIVITY_TIMEOUT_USEC ) ? FALSE : TRUE );
2271
2272}	// BOOL CDspCommObject::IsMidiOutActive()
2273
2274
2275#endif // MIDI_SUPPORT
2276
2277
2278
2279// **** CDspCommObject.cpp ****
2280