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