xref: /haiku/src/add-ons/kernel/drivers/audio/echo/generic/CEchoGals_transport.cpp (revision fef6144999c2fa611f59ee6ffe6dd7999501385c)
1 // ****************************************************************************
2 //
3 //		CEchoGals_transport.cpp
4 //
5 //		Audio transport methods for the CEchoGals driver class.
6 //		Set editor tabs to 3 for your viewing pleasure.
7 //
8 // ----------------------------------------------------------------------------
9 //
10 // This file is part of Echo Digital Audio's generic driver library.
11 // Copyright Echo Digital Audio Corporation (c) 1998 - 2005
12 // All rights reserved
13 // www.echoaudio.com
14 //
15 // This library is free software; you can redistribute it and/or
16 // modify it under the terms of the GNU Lesser General Public
17 // License as published by the Free Software Foundation; either
18 // version 2.1 of the License, or (at your option) any later version.
19 //
20 // This library is distributed in the hope that it will be useful,
21 // but WITHOUT ANY WARRANTY; without even the implied warranty of
22 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
23 // Lesser General Public License for more details.
24 //
25 // You should have received a copy of the GNU Lesser General Public
26 // License along with this library; if not, write to the Free Software
27 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
28 //
29 // ****************************************************************************
30 
31 #include "CEchoGals.h"
32 
33 #pragma optimize("",off)
34 
35 
36 /******************************************************************************
37 
38  Functions for opening and closing pipes
39 
40  ******************************************************************************/
41 
42 //===========================================================================
43 //
44 // OpenAudio is used to reserve audio pipes for your exclusive use.  The call
45 // will fail if someone else has already opened the pipes.  Calling OpenAudio
46 // is the first step if you want to play or record.
47 //
48 // If the fCheckHardware flag is true, then the open will fail
49 // if the DSP and ASIC firmware have not been loaded (usually means
50 // your external box is turned off).
51 //
52 //===========================================================================
53 
54 ECHOSTATUS CEchoGals::OpenAudio
55 (
56 	PECHOGALS_OPENAUDIOPARAMETERS	pOpenParameters,	// Info on pipe
57 	PWORD									pwPipeIndex,		// Pipe index ptr
58 	BOOL									fCheckHardware,
59 	CDaffyDuck							*pDuck
60 )
61 {
62 	CChannelMask	cmMask;
63 	WORD				wPipeMax, wPipe, wPipeIndex, i, wWidth;
64 	ECHOSTATUS 		Status;
65 
66 	ECHO_DEBUGPRINTF( ("CEchoGals::OpenAudio: %s %u "
67 							 "PipeWidth %d "
68 							 "Cyclic %u \n",
69 							 ( pOpenParameters->Pipe.bIsInput ) ? "Input" : "Output",
70 								pOpenParameters->Pipe.nPipe,
71 								pOpenParameters->Pipe.wInterleave,
72 								pOpenParameters->bIsCyclic) );
73 
74 	*pwPipeIndex = (WORD) -1;		// So it's never undefined
75 
76 	//
77 	// Make sure the hardware is OK
78 	//
79 	if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() )
80 	{
81 		ECHO_DEBUGPRINTF( ("\tECHOSTATUS_DSP_DEAD\n") );
82 		return ECHOSTATUS_DSP_DEAD;
83 	}
84 
85 	//
86 	// Make sure the DSP & ASIC are up and running
87 	//	 - only if fCheckHardware is true
88 	//
89 	if (fCheckHardware)
90 	{
91 		Status = GetDspCommObject()->LoadFirmware();
92 
93 		if ( ECHOSTATUS_OK != Status )
94 			return Status;
95 	}
96 
97 	//
98 	// Validate the pipe number
99 	//
100 	wPipe = pOpenParameters->Pipe.nPipe;
101 	wWidth = pOpenParameters->Pipe.wInterleave;
102 
103 	if ( pOpenParameters->Pipe.bIsInput )
104 	{
105 		wPipeIndex = wPipe + GetNumPipesOut();
106 		wPipeMax = GetNumPipesIn();
107 	}
108 	else
109 	{
110 		wPipeIndex = wPipe;
111 		wPipeMax = GetNumPipesOut();
112 	}
113 
114 	if ( ( wPipe + wWidth ) > wPipeMax )
115 	{
116 		ECHO_DEBUGPRINTF( ("\tECHOSTATUS_INVALID_CHANNEL\n") );
117 		return ECHOSTATUS_INVALID_CHANNEL;
118 	}
119 
120 	//
121 	// If the width is more than two, make sure that this card
122 	// can handle super interleave
123 	//
124 	if (	(0 == (m_wFlags & ECHOGALS_ROFLAG_SUPER_INTERLEAVE_OK)) &&
125 			(wWidth > 2)
126 		)
127 	{
128 		ECHO_DEBUGPRINTF( ("\tECHOSTATUS_NO_SUPER_INTERLEAVE\n") );
129 		return ECHOSTATUS_NO_SUPER_INTERLEAVE;
130 	}
131 
132 	//
133 	// See if the specified pipes are already open
134 	//
135 	for ( i = 0; i < pOpenParameters->Pipe.wInterleave; i++ )
136 	{
137 		cmMask.SetIndexInMask( wPipeIndex + i );
138 	}
139 
140 	if ( m_cmAudioOpen.Test( &cmMask ) )
141 	{
142 		ECHO_DEBUGPRINTF( ("\tECHOSTATUS_CHANNEL_ALREADY_OPEN\n") );
143 		return( ECHOSTATUS_CHANNEL_ALREADY_OPEN );
144 	}
145 
146 	//
147 	// Make a daffy duck
148 	//
149 	if (NULL == pDuck)
150 	{
151 		pDuck = MakeDaffyDuck();
152 
153 		if (NULL == pDuck)
154 			return ECHOSTATUS_NO_MEM;
155 	}
156 
157 	SetDaffyDuck( wPipeIndex, pDuck );
158 
159 
160 	//
161 	// Reset the 64-bit DMA position
162 	//
163 	ResetDmaPos(wPipeIndex);
164 	GetDspCommObject()->ResetPipePosition(wPipeIndex);
165 
166 	//
167 	// Prep stuff
168 	//
169 	m_cmAudioOpen += cmMask;
170 	if ( pOpenParameters->bIsCyclic )
171 		m_cmAudioCyclic += cmMask;
172 	m_Pipes[ wPipeIndex ] = pOpenParameters->Pipe;
173 	*pwPipeIndex = wPipeIndex;
174 	m_ProcessId[ wPipeIndex ] = pOpenParameters->ProcessId;
175 	Reset( wPipeIndex );
176 
177 	ECHO_DEBUGPRINTF( ("\tECHOSTATUS_OK\n") );
178 	return ECHOSTATUS_OK;
179 
180 }	// ECHOSTATUS CEchoGals::OpenAudio
181 
182 
183 //===========================================================================
184 //
185 // CloseAudio is, naturally, the inverse of OpenAudio.
186 //
187 //===========================================================================
188 
189 ECHOSTATUS CEchoGals::CloseAudio
190 (
191 	PECHOGALS_CLOSEAUDIOPARAMETERS	pCloseParameters,
192 	BOOL										fFreeDuck
193 )
194 {
195 	CChannelMask	cmMask;
196 	ECHOSTATUS		Status;
197 	WORD				i;
198 	WORD				wPipeIndex;
199 
200 	wPipeIndex = pCloseParameters->wPipeIndex;
201 
202 	ECHO_DEBUGPRINTF( ("CEchoGals::CloseAudio: Pipe %u  ",
203 							 wPipeIndex) );
204 
205 	Status = VerifyAudioOpen( wPipeIndex );
206 	if ( ECHOSTATUS_OK != Status )
207 		return Status;
208 
209 	for ( i = 0;
210 			i < m_Pipes[ wPipeIndex ].wInterleave;
211 			i++ )
212 	{
213 		cmMask.SetIndexInMask( wPipeIndex + i );
214 	}
215 	Reset( wPipeIndex );
216 
217 	//
218 	// Free the scatter-gather list
219 	//
220 	if (NULL != m_DaffyDucks[wPipeIndex])
221 	{
222 		if (fFreeDuck)
223 			delete m_DaffyDucks[wPipeIndex];
224 
225 		m_DaffyDucks[wPipeIndex] = NULL;
226 	}
227 
228 	m_cmAudioOpen -= cmMask;
229 	m_cmAudioCyclic -= cmMask;
230 
231 	wPipeIndex = wPipeIndex;
232 	m_ProcessId[ wPipeIndex ] = NULL;
233 	m_Pipes[ wPipeIndex ].wInterleave = 0;
234 
235 	ECHO_DEBUGPRINTF( ("\tECHOSTATUS_OK\n") );
236 	return ECHOSTATUS_OK;
237 
238 }	// ECHOSTATUS CEchoGals::CloseAudio
239 
240 
241 //===========================================================================
242 //
243 // VerifyAudioOpen is a utility function; it tells you if
244 // a pipe is open or not.
245 //
246 //===========================================================================
247 
248 ECHOSTATUS CEchoGals::VerifyAudioOpen
249 (
250 	WORD		wPipeIndex
251 )
252 {
253 	CChannelMask	cmMask;
254 
255 	if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() )
256 	{
257 		ECHO_DEBUGPRINTF( ("\tECHOSTATUS_DSP_DEAD\n") );
258 		return ECHOSTATUS_DSP_DEAD;
259 	}
260 
261 	cmMask.SetIndexInMask( wPipeIndex );
262 	if ( !( m_cmAudioOpen.Test( &cmMask ) ) )
263 	{
264 		ECHO_DEBUGPRINTF( ("\tECHOSTATUS_CHANNEL_NOT_OPEN\n") );
265 		return ECHOSTATUS_CHANNEL_NOT_OPEN;
266 	}
267 
268 	return ECHOSTATUS_OK;
269 
270 }	// ECHOSTATUS CEchoGals::VerifyAudioOpen
271 
272 
273 //===========================================================================
274 //
275 // GetActivePipes tells you which pipes are currently active; that is, which
276 // pipes are currently playing or recording.
277 //
278 //===========================================================================
279 
280 ECHOSTATUS CEchoGals::GetActivePipes
281 (
282 	PCChannelMask	pChannelMask
283 )
284 {
285 	if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() )
286 		return ECHOSTATUS_DSP_DEAD;
287 
288 	GetDspCommObject()->GetActivePipes( pChannelMask );
289 	return ECHOSTATUS_OK;
290 }	// void CEchoGals::GetActivePipes()
291 
292 
293 //===========================================================================
294 //
295 // Just like GetActivePipes, but this one tells you which pipes are currently
296 // open.
297 //
298 //===========================================================================
299 
300 ECHOSTATUS CEchoGals::GetOpenPipes
301 (
302 	PCChannelMask	pChannelMask
303 )
304 {
305 	if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() )
306 		return ECHOSTATUS_DSP_DEAD;
307 
308 	*pChannelMask = m_cmAudioOpen;
309 	return ECHOSTATUS_OK;
310 
311 }	// void CEchoGals::GetOpenPipes()
312 
313 
314 
315 
316 /******************************************************************************
317 
318  Functions for setting audio formats and the sample rate
319 
320  ******************************************************************************/
321 
322 //===========================================================================
323 //
324 // Validate an audio format.
325 //
326 // For comments on audio formats, refer to the definition of
327 // ECHOGALS_AUDIOFORMAT.
328 //
329 //===========================================================================
330 
331 ECHOSTATUS CEchoGals::QueryAudioFormat
332 (
333 	WORD							wPipeIndex,
334 	PECHOGALS_AUDIOFORMAT	pAudioFormat
335 )
336 {
337 	ECHOSTATUS Status = ECHOSTATUS_OK;
338 
339 	ECHO_DEBUGPRINTF( ("CEchoGals::QueryAudioFormat:\n") );
340 
341 	//
342 	// If this pipe is open, make sure that this audio format
343 	// does not exceed the stored pipe width
344 	//
345 	WORD wInterleave = pAudioFormat->wDataInterleave;
346 	WORD wStoredPipeWidth = m_Pipes[ wPipeIndex ].wInterleave;
347 
348 	if (0 != wStoredPipeWidth)
349 	{
350 		if (wInterleave > wStoredPipeWidth)
351 		{
352 			ECHO_DEBUGPRINTF(("CEchoGals::QueryAudioFormat - pipe was opened "
353 									"with a width of %d; interleave of %d invalid.\n",
354 									wStoredPipeWidth,
355 									pAudioFormat->wDataInterleave));
356 			return ECHOSTATUS_BAD_FORMAT;
357 		}
358 	}
359 
360 	//
361 	// Check for super interleave (i.e. interleave > 2)
362 	//
363 	if (wInterleave > 2)
364 	{
365 		//
366 		// Make sure the card is capable of super interleave
367 		//
368 		if (0 == (m_wFlags & ECHOGALS_ROFLAG_SUPER_INTERLEAVE_OK))
369 			return ECHOSTATUS_NO_SUPER_INTERLEAVE;
370 
371 		//
372 		// Interleave must be even & data must be little endian
373 		//
374 		if (	(0 != pAudioFormat->byDataAreBigEndian) ||
375 			  	(0 != (wInterleave & 1)	)
376 			)
377 			return ECHOSTATUS_BAD_FORMAT;
378 
379 		//
380 		// 16, 24, or 32 bit samples are required
381 		//
382 		if ( 	(32 != pAudioFormat->wBitsPerSample) &&
383 				(24 != pAudioFormat->wBitsPerSample) &&
384 				(16 != pAudioFormat->wBitsPerSample) )
385 			return ECHOSTATUS_BAD_FORMAT;
386 
387 
388 		//
389 		// Make sure that this interleave factor on this pipe
390 		// does not exceed the number of pipes for the card
391 		//
392 		WORD wMaxPipe;
393 
394 		if (wPipeIndex >= GetNumPipesOut())
395 		{
396 			wMaxPipe = GetNumPipesIn();
397 			wPipeIndex -= GetNumPipesOut();
398 		}
399 		else
400 		{
401 			wMaxPipe = GetNumPipesOut();
402 		}
403 
404 		if ( (wPipeIndex + wInterleave) > wMaxPipe)
405 			return ECHOSTATUS_BAD_FORMAT;
406 
407 		return ECHOSTATUS_OK;
408 	}
409 
410 	//
411 	// Check the interleave
412 	//
413 	if ( 	(1 != pAudioFormat->wDataInterleave) &&
414 			(2 != pAudioFormat->wDataInterleave) )
415 
416 	{
417 		ECHO_DEBUGPRINTF(	("CEchoGals::QueryAudioFormat - interleave %d not allowed\n",
418 								pAudioFormat->wDataInterleave));
419 		return ECHOSTATUS_BAD_FORMAT;
420 	}
421 
422 	//
423 	//	If the big endian flag is set, the data must be mono or stereo interleave,
424 	// 32 bits wide, left justified data.  Only the upper 24 bits are used.
425 	//
426 	if (pAudioFormat->byDataAreBigEndian)
427 	{
428 		//
429 		// Must have 32 bits per sample
430 		//
431 		if (pAudioFormat->wBitsPerSample != 32)
432 		{
433 			ECHO_DEBUGPRINTF(("CEchoGals::QueryAudioFormat - Only 32 bits per"
434 									" sample supported for big-endian data\n"));
435 			return ECHOSTATUS_BAD_FORMAT;
436 		}
437 
438 		//
439 		// Mono or stereo only
440 		//
441 		switch (pAudioFormat->wDataInterleave)
442 		{
443 
444 #ifdef STEREO_BIG_ENDIAN32_SUPPORT
445 
446 			case 1 :
447 			case 2 :
448 				break;
449 #else
450 
451 			case 1 :
452 				break;
453 
454 #endif
455 
456 			default :
457 				ECHO_DEBUGPRINTF(("CEchoGals::QueryAudioFormat - Interleave of %d"
458 										" not allowed for big-endian data\n",
459 										pAudioFormat->wDataInterleave));
460 				return ECHOSTATUS_BAD_FORMAT;
461 		}
462 
463 		return ECHOSTATUS_OK;
464 	}
465 
466 	//
467 	// Check bits per sample
468 	//
469 	switch ( pAudioFormat->wBitsPerSample )
470 	{
471 		case 8 :
472 		case 16 :
473 		case 24 :
474 		case 32 :
475 			break;
476 
477 		default :
478 			ECHO_DEBUGPRINTF(
479 				("CEchoGals::QueryAudioFormat: No valid format "
480 				 "specified, bits per sample %d\n",
481 				 pAudioFormat->wBitsPerSample) );
482 			Status = ECHOSTATUS_BAD_FORMAT;
483 			break;
484 	}
485 
486 	return Status;
487 
488 }	// ECHOSTATUS CEchoGals::QueryAudioFormat
489 
490 
491 //===========================================================================
492 //
493 // SetAudioFormat sets the format of the audio data in host memory
494 // for this pipe.
495 //
496 //===========================================================================
497 
498 ECHOSTATUS CEchoGals::SetAudioFormat
499 (
500 	WORD							wPipeIndex,
501 	PECHOGALS_AUDIOFORMAT	pAudioFormat
502 )
503 {
504 	ECHOSTATUS	Status;
505 
506 	ECHO_DEBUGPRINTF( ("CEchoGals::SetAudioFormat: "
507 							 "for pipe %d\n",
508 							 wPipeIndex) );
509 
510 	//
511 	// Make sure this pipe is open
512 	//
513 	Status = VerifyAudioOpen( wPipeIndex );
514 	if ( ECHOSTATUS_OK != Status )
515 		return Status;
516 
517 	//
518 	// Check the format
519 	//
520 	Status = QueryAudioFormat( wPipeIndex, pAudioFormat );
521 	if ( ECHOSTATUS_OK != Status )
522 		return Status;
523 
524 	//
525 	// Set the format
526 	//
527 	Status = GetDspCommObject()->SetAudioFormat( wPipeIndex, pAudioFormat );
528 	return Status;
529 
530 }	// ECHOSTATUS CEchoGals::SetAudioFormat - single pipe
531 
532 
533 //===========================================================================
534 //
535 // This call lets you set the audio format for several pipes at once.
536 //
537 //===========================================================================
538 
539 ECHOSTATUS CEchoGals::SetAudioFormat
540 (
541 	PCChannelMask				pChannelMask,
542 	PECHOGALS_AUDIOFORMAT	pAudioFormat
543 )
544 {
545 	WORD			wPipeIndex = 0xffff;
546 	ECHOSTATUS	Status;
547 
548 	if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() )
549 	{
550 		ECHO_DEBUGPRINTF( ("\tECHOSTATUS_DSP_DEAD\n") );
551 		return ECHOSTATUS_DSP_DEAD;
552 	}
553 
554 	for ( ; ; )
555 	{
556 		wPipeIndex = pChannelMask->GetIndexFromMask( ++wPipeIndex );
557 		if ( (WORD) ECHO_INVALID_CHANNEL == wPipeIndex )
558 			break;							// We be done!
559 
560 		//
561 		// See if this pipe is open
562 		//
563 		if ( !( m_cmAudioOpen.TestIndexInMask( wPipeIndex ) ) )
564 		{
565 			ECHO_DEBUGPRINTF( ("CEchoGals::SetAudioFormat: "
566 									 "for pipe %d failed, pipe not open\n",
567 									 wPipeIndex) );
568 			return ECHOSTATUS_CHANNEL_NOT_OPEN;
569 		}
570 
571 		//
572 		// See if the format is OK
573 		//
574 		ECHO_DEBUGPRINTF( ("CEchoGals::SetAudioFormat: "
575 								 "for pipe %d\n",
576 								 wPipeIndex) );
577 		Status = QueryAudioFormat( wPipeIndex, pAudioFormat );
578 		if ( ECHOSTATUS_OK != Status )
579 			return Status;
580 
581 		//
582 		// Set the format for this pipe
583 		//
584 		Status = GetDspCommObject()->SetAudioFormat( wPipeIndex, pAudioFormat );
585 		if ( ECHOSTATUS_OK != Status )
586 			return Status;
587 
588 		m_wBytesPerSample[ wPipeIndex ] = pAudioFormat->wBitsPerSample / 8;
589 		m_wBytesPerSample[ wPipeIndex ] *= pAudioFormat->wDataInterleave;
590 	}
591 
592 	return ECHOSTATUS_OK;
593 
594 }	// ECHOSTATUS CEchoGals::SetAudioFormat - multiple pipes
595 
596 
597 //===========================================================================
598 //
599 // GetAudioFormat returns the current audio format for a pipe.
600 //
601 //===========================================================================
602 
603 ECHOSTATUS CEchoGals::GetAudioFormat
604 (
605 	WORD							wPipeIndex,
606 	PECHOGALS_AUDIOFORMAT	pAudioFormat
607 )
608 {
609 	ECHO_DEBUGPRINTF( ("CEchoGals::GetAudioFormat: "
610 							 "for pipe %d\n",
611 							 wPipeIndex) );
612 
613 	GetDspCommObject()->GetAudioFormat( wPipeIndex, pAudioFormat );
614 
615 	return ECHOSTATUS_OK;
616 
617 }	// ECHOSTATUS CEchoGals::GetAudioFormat
618 
619 
620 //===========================================================================
621 //
622 // This function does exactly what you think it does.
623 //
624 // Note that if the card is not set to internal clock (that is, the hardware
625 // is synced to word clock or some such), this call has no effect.
626 //
627 // Note that all of the inputs and outputs on a single card share the same
628 // clock.
629 //
630 //===========================================================================
631 
632 ECHOSTATUS CEchoGals::SetAudioSampleRate
633 (
634 	DWORD		dwSampleRate
635 )
636 {
637 	ECHOSTATUS	Status;
638 
639 	ECHO_DEBUGPRINTF( ("CEchoGals::SetAudioSampleRate: "
640 							 "to %ld Hz\n",
641 							 dwSampleRate) );
642 
643 	//
644 	// Check to see if the sample rate is locked
645 	//
646 	if ( 0 != (m_wFlags & ECHOGALS_FLAG_SAMPLE_RATE_LOCKED))
647 	{
648 			return ECHOSTATUS_OK;
649 	}
650 	else
651 	{
652 		Status = QueryAudioSampleRate( dwSampleRate );
653 		if ( ECHOSTATUS_OK != Status )
654 			return Status;
655 
656 		if ( dwSampleRate == GetDspCommObject()->SetSampleRate( dwSampleRate ) )
657 		{
658 			m_dwSampleRate = dwSampleRate;
659 			return ECHOSTATUS_OK;
660 		}
661 	}
662 	return ECHOSTATUS_BAD_FORMAT;
663 
664 }	// ECHOSTATUS CEchoGals::SetAudioSampleRate
665 
666 
667 //===========================================================================
668 //
669 // GetAudioSampleRate - retrieves the current sample rate for the hardware
670 //
671 //===========================================================================
672 
673 ECHOSTATUS CEchoGals::GetAudioSampleRate
674 (
675 	PDWORD	pdwSampleRate
676 )
677 {
678 	ECHO_DEBUGPRINTF( ("CEchoGals::GetAudioSampleRate\n"));
679 
680 	*pdwSampleRate = m_dwSampleRate;
681 
682 	return ECHOSTATUS_OK;
683 
684 }	// ECHOSTATUS CEchoGals::GetAudioSampleRate
685 
686 
687 
688 
689 /******************************************************************************
690 
691  Functions related to the scatter-gather list
692 
693  ******************************************************************************/
694 
695 //===========================================================================
696 //
697 // This method is used to create a CDaffyDuck object to
698 // manage a scatter-gather list for a newly opened pipe.
699 //
700 //===========================================================================
701 
702 CDaffyDuck * CEchoGals::MakeDaffyDuck()
703 {
704 	ECHOSTATUS 	Status = ECHOSTATUS_OK;
705 	CDaffyDuck 	*pDuck;
706 
707 	//---------------------------------------------------------
708 	//
709 	// Allocate the main daffy duck for this pipe
710 	//
711 	//---------------------------------------------------------
712 
713 	pDuck = new CDaffyDuck(	m_pOsSupport );
714 
715 	//
716 	// Check the daffy duck
717 	//
718 	if (pDuck)
719 	{
720 		Status = pDuck->InitCheck();
721 		if (ECHOSTATUS_OK != Status)
722 		{
723 			delete pDuck;
724 			return NULL;
725 		}
726 	}
727 
728 	return pDuck;
729 
730 }	// MakeDaffyDuck
731 
732 
733 
734 
735 //===========================================================================
736 //
737 // Use the given CDaffyDuck object as the scatter-gather list for this pipe
738 //
739 //===========================================================================
740 
741 ECHOSTATUS CEchoGals::SetDaffyDuck(WORD wPipeIndex, CDaffyDuck *pDuck)
742 {
743 	m_DaffyDucks[wPipeIndex] = pDuck;
744 
745 	return ECHOSTATUS_OK;
746 
747 }	// SetDaffyDuck
748 
749 
750 
751 
752 //===========================================================================
753 //
754 // This method returns a pointer to the daffy duck for a pipe; the caller
755 // can then have direct access to the daffy duck object.
756 //
757 //===========================================================================
758 
759 CDaffyDuck *CEchoGals::GetDaffyDuck(WORD wPipeIndex)
760 {
761 	ECHO_DEBUGPRINTF(("CEchoGals::GetDaffyDuck for pipe index %d\n",wPipeIndex));
762 
763 	if (wPipeIndex >= GetNumPipes())
764 		return NULL;
765 
766 	return m_DaffyDucks[wPipeIndex];
767 }
768 
769 
770 
771 /******************************************************************************
772 
773  Functions for starting and stopping transport
774 
775  ******************************************************************************/
776 
777 //===========================================================================
778 //
779 //	Start transport for a single pipe
780 //
781 //===========================================================================
782 
783 ECHOSTATUS CEchoGals::Start
784 (
785 	WORD	wPipeIndex
786 )
787 {
788 	CChannelMask	cmMask;
789 
790 	cmMask.SetIndexInMask( wPipeIndex );
791 	return Start( &cmMask );
792 
793 }	// ECHOSTATUS CEchoGals::Start
794 
795 
796 //===========================================================================
797 //
798 //	Start transport for a group of pipes
799 //
800 // This function includes logic to sync-start several pipes at once,
801 // according to the process ID specified when the pipe was opened.  This is
802 // included to work around a limitation of the Windows wave API so that
803 // programs could use multiple inputs and outputs and have them start at the
804 // same time.
805 //
806 // If you don't want to use this feature, call CEchoGals::ClearFlags
807 // with ECHOGALS_FLAG_SYNCH_WAVE and the pipes will start immediately.
808 //
809 //===========================================================================
810 
811 ECHOSTATUS CEchoGals::Start
812 (
813 	PCChannelMask	pChannelMask
814 )
815 {
816 	WORD				wPipe;
817 	DWORD				dwPhysStartAddr;
818 	CChannelMask	cmStart;
819 	PVOID				ProcessId = NULL;
820 	CDspCommObject *pDCO;
821 
822 	pDCO = GetDspCommObject();
823 	if ( NULL == pDCO || pDCO->IsBoardBad() )
824 		return ECHOSTATUS_DSP_DEAD;
825 
826 	//
827 	//	See if we are dealing with synchronized wave pipes.  If the sync
828 	// flag is set, get the process ID for this pipe to compare with
829 	// other pipes.
830 	//
831 	if ( GetFlags() & ECHOGALS_FLAG_SYNCH_WAVE )
832 	{
833 		wPipe = pChannelMask->GetIndexFromMask( 0 );
834 		ProcessId = m_ProcessId[ wPipe ];
835 	}
836 
837 	//--------------------------------------------------------
838 	// Process each pipe in the mask
839 	//--------------------------------------------------------
840 
841 	for (wPipe = 0; wPipe < GetNumPipes(); wPipe++)
842 	{
843 		PDWORD pdwDspCommPositions;
844 
845 		//
846 		// Skip this pipe if it's not in the mask
847 		//
848 		if (!pChannelMask->TestIndexInMask(wPipe))
849 			continue;
850 
851 		//
852 		// This pipe must have a CDaffyDuck object
853 		//
854 		if (NULL == m_DaffyDucks[ wPipe ])
855 		{
856 			ECHO_DEBUGPRINTF(("CDaffyDuck::Start - trying to start pipe index %d "
857 									"but there is no CDaffyDuck!\n",wPipe));
858 			return ECHOSTATUS_CHANNEL_NOT_OPEN;
859 		}
860 
861 		//
862 		// If this pipe was opened in cyclic mode, make sure that the corresponding
863 		// CDaffyDuck has been wrapped
864 		//
865 		if (	(0 != m_cmAudioCyclic.TestIndexInMask( wPipe ) ) &&
866 				(FALSE == m_DaffyDucks[wPipe]->Wrapped())
867 			)
868 		{
869 			ECHO_DEBUGPRINTF(("CEchoGals::Start called for pipe index %d - "
870 									"pipe was opened in cyclic mode, but the duck "
871 									"has not been wrapped\n",wPipe));
872 			return ECHOSTATUS_DUCK_NOT_WRAPPED;
873 		}
874 
875 		//
876 		// Set the physical start address for the duck for this pipe
877 		//
878 		dwPhysStartAddr = m_DaffyDucks[wPipe]->GetPhysStartAddr();
879 		pDCO->SetAudioDuckListPhys( wPipe, dwPhysStartAddr );
880 
881 
882 		//
883 		// Do different things to this pipe depending on the
884 		// state
885 		//
886 		switch (m_byPipeState[wPipe])
887 		{
888 			case PIPE_STATE_RESET :
889 				//
890 				// Clean up the DMA position stuff
891 				//
892 				pdwDspCommPositions = pDCO->GetAudioPositionPtr();
893 				pdwDspCommPositions[ wPipe ] = 0;
894 
895 				//
896 				// If this pipe isn't synced or is in a reset state,
897 				// start it up
898 				//
899 				if (NULL == ProcessId)
900 				{
901 					m_byPipeState[ wPipe ] = PIPE_STATE_STARTED;
902 					cmStart.SetIndexInMask( wPipe );
903 				}
904 				else
905 				{
906 					//
907 					// This pipe is synced; upgrade to PENDING
908 					//
909 					m_byPipeState[ wPipe ] = PIPE_STATE_PENDING;
910 				}
911 				break;
912 
913 
914 			case PIPE_STATE_STOPPED :
915 
916 				if (NULL == ProcessId)
917 				{
918 					//
919 					// Non-synced pipe; start 'er up!
920 					//
921 					m_byPipeState[ wPipe ] = PIPE_STATE_STARTED;
922 					cmStart.SetIndexInMask( wPipe );
923 				}
924 				else
925 				{
926 					//
927 					// Synced pipe; if this pipe is in STOP mode,
928 					// upgrade it to PENDING status
929 					//
930 					m_byPipeState[ wPipe ] = PIPE_STATE_PENDING;
931 				}
932 				break;
933 
934 
935 			case PIPE_STATE_PENDING :
936 			case PIPE_STATE_STARTED :
937 				break;
938 		}
939 	}
940 
941 	//-----------------------------------------------------------------
942 	// Start the pipes
943 	//-----------------------------------------------------------------
944 
945 	//
946 	// Don't go if all the synced pipes are not yet pending
947 	//
948 	BOOL	fAllReady = TRUE;
949 	for ( wPipe = 0; wPipe < GetNumPipes(); wPipe++ )
950 	{
951 		if ( 	( ProcessId == m_ProcessId[ wPipe ] ) &&
952 				( PIPE_STATE_STOPPED == m_byPipeState[wPipe]))
953 		{
954 			ECHO_DEBUGPRINTF(("CEchoGals::Start - can't start; pipe %d "
955 									"still set to state %d\n",
956 									wPipe,
957 									m_byPipeState[wPipe]));
958 			fAllReady = FALSE;
959 		}
960 	}
961 
962 	//
963 	// All synced pipes are pending; time to go!
964 	//
965 	if (fAllReady)
966 	{
967 		for (wPipe = 0; wPipe < GetNumPipes(); wPipe++)
968 		{
969 			if ( 	(ProcessId == m_ProcessId[ wPipe ]) &&
970 					(PIPE_STATE_PENDING == m_byPipeState[ wPipe ]))
971 			{
972 				m_byPipeState[wPipe] = PIPE_STATE_STARTED;
973 				cmStart.SetIndexInMask( wPipe );
974 				ECHO_DEBUGPRINTF(("CEchoGals::Start - setting pipe %d to start\n",
975 										wPipe));
976 			}
977 		}
978 	}
979 
980 	if ( cmStart.IsEmpty() )
981 		return ECHOSTATUS_OK;
982 
983 
984 	//-----------------------------------------------------------------
985 	// Time to go
986 	//-----------------------------------------------------------------
987 
988 	return pDCO->StartTransport( &cmStart );
989 
990 }	// ECHOSTATUS CEchoGals::Start
991 
992 
993 
994 //===========================================================================
995 //
996 // Stop a single pipe
997 //
998 //===========================================================================
999 
1000 ECHOSTATUS CEchoGals::Stop
1001 (
1002 	WORD	wPipeIndex
1003 )
1004 {
1005 	CChannelMask	cmMask;
1006 
1007 	cmMask.SetIndexInMask( wPipeIndex );
1008 	return( Stop( &cmMask ) );
1009 
1010 }	// ECHOSTATUS CEchoGals::Stop
1011 
1012 
1013 //===========================================================================
1014 //
1015 // Stop several pipes simultaneously
1016 //
1017 //===========================================================================
1018 
1019 ECHOSTATUS CEchoGals::Stop
1020 (
1021 	PCChannelMask	pChannelMask
1022 )
1023 {
1024 	INT32			i;
1025 	ECHOSTATUS	Status;
1026 
1027 	if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() )
1028 		return ECHOSTATUS_DSP_DEAD;
1029 
1030 	Status = GetDspCommObject()->StopTransport( pChannelMask );
1031 	if ( ECHOSTATUS_OK != Status )
1032 		return Status;
1033 
1034 	for ( i = 0; i < GetNumPipes(); i++ )
1035 	{
1036 		//
1037 		//	Skip channel if not in mask
1038 		//
1039 		if ( !pChannelMask->TestIndexInMask( (WORD) i ) )
1040 			continue;
1041 
1042 
1043 		//
1044 		// Don't bother if it's stopped already
1045 		//
1046 		if ( PIPE_STATE_STOPPED == m_byPipeState[ i ] )
1047 			continue;
1048 
1049 		//
1050 		// Muck with the DMA position
1051 		//
1052 		UpdateDmaPos( (WORD) i );
1053 
1054 		m_byPipeState[ i ] = PIPE_STATE_STOPPED;
1055 	}
1056 
1057 	return Status;
1058 
1059 }	// ECHOSTATUS CEchoGals::Stop
1060 
1061 
1062 //===========================================================================
1063 //
1064 // Reset transport for a single pipe
1065 //
1066 //===========================================================================
1067 
1068 ECHOSTATUS CEchoGals::Reset
1069 (
1070 	WORD	wPipeIndex
1071 )
1072 {
1073 	CChannelMask	cmMask;
1074 
1075 	cmMask.SetIndexInMask( wPipeIndex );
1076 	return Reset( &cmMask );
1077 
1078 }	// ECHOSTATUS CEchoGals::Reset
1079 
1080 
1081 //===========================================================================
1082 //
1083 // Reset transport for a group of pipes simultaneously
1084 //
1085 //===========================================================================
1086 
1087 ECHOSTATUS CEchoGals::Reset
1088 (
1089 	PCChannelMask	pChannelMask
1090 )
1091 {
1092 	WORD			i;
1093 	ECHOSTATUS	Status;
1094 
1095 	if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() )
1096 		return ECHOSTATUS_DSP_DEAD;
1097 
1098 	Status = GetDspCommObject()->ResetTransport( pChannelMask );
1099 	if ( ECHOSTATUS_OK != Status )
1100 		return Status;
1101 
1102 	for ( i = 0; i < GetNumPipes(); i++ )
1103 	{
1104 		//
1105 		//	Skip channel if not in mask
1106 		//
1107 		if ( !pChannelMask->TestIndexInMask( (WORD) i ) )
1108 			continue;
1109 
1110 		if ( PIPE_STATE_RESET == m_byPipeState[ i ] )
1111 			continue;
1112 
1113 		//
1114 		// Muck with the DMA position
1115 		//
1116 		UpdateDmaPos( i );
1117 		m_dwLastDspPos[ i ] = 0;
1118 		GetDspCommObject()->ResetPipePosition(i);
1119 
1120 		m_byPipeState[ i ] = PIPE_STATE_RESET;
1121 	}
1122 
1123 	return Status;
1124 
1125 }	// ECHOSTATUS CEchoGals::Reset
1126 
1127 
1128 
1129 
1130 /******************************************************************************
1131 
1132  Functions for handling the current DMA position for pipes; the DMA position
1133  is the number of bytes transported by a pipe.
1134 
1135  ******************************************************************************/
1136 
1137 //===========================================================================
1138 //
1139 // The DSP sends back a 32 bit DMA counter for each pipe of the number of bytes
1140 // transported; this count is written by the DSP to the comm page without
1141 // the driver doing anything.
1142 //
1143 // The driver then maintains a 64 bit counter based off of the DSP's counter.
1144 //
1145 // Call UpdateDmaPos to cause the driver to update the internal 64 bit DMA
1146 // counter.
1147 //
1148 // The 64 bit DMA counter is in units of bytes, not samples.
1149 //
1150 //===========================================================================
1151 
1152 void CEchoGals::UpdateDmaPos( WORD wPipeIndex )
1153 {
1154 	DWORD dwDspPos;
1155 	DWORD dwDelta;
1156 
1157 	//
1158 	// Get the current DSP position and find out how much it
1159 	// has moved since last time.  This is necessary to avoid
1160 	// the 32 bit counter wrapping around.
1161 	//
1162 	dwDspPos = GetDspCommObject()->GetAudioPosition( wPipeIndex );
1163 	dwDelta = dwDspPos - m_dwLastDspPos[ wPipeIndex ];
1164 
1165 	//
1166 	// Adjust the 64 bit position
1167 	//
1168 	m_ullDmaPos[ wPipeIndex ] += dwDelta;
1169 	m_dwLastDspPos[ wPipeIndex ] = dwDspPos;
1170 
1171 } // UpdateDmaPos
1172 
1173 
1174 //===========================================================================
1175 //
1176 // ResetDmaPos resets the 64 bit DMA counter.
1177 //
1178 //===========================================================================
1179 
1180 void CEchoGals::ResetDmaPos(WORD wPipe)
1181 {
1182 	m_ullDmaPos[ wPipe ] = 0;
1183 	m_dwLastDspPos[ wPipe ] = 0;
1184 
1185 	//
1186 	// There may still be mappings in the daffy duck; if so,
1187 	// tell them to reset their DMA positions starting at zero
1188 	//
1189 	if (NULL != m_DaffyDucks[wPipe])
1190 		m_DaffyDucks[wPipe]->ResetStartPos();
1191 }
1192 
1193 
1194 //===========================================================================
1195 //
1196 // This is a very powerful feature; calling this function gives you a pointer
1197 // to the memory location where the DSP writes the 32 bit DMA position for
1198 // a pipe.  The DSP is constantly updating this value as it moves data.
1199 //
1200 // Since the DSP is constantly updating it, you can dereference this pointer
1201 // from anywhere and read the DMA position without calling the generic driver.
1202 // This means that with some adroit mapping, you could read the DMA position
1203 // from user mode without calling the kernel.
1204 //
1205 // Remember, Peter - with great power comes great responsibility; you should
1206 // only read this pointer and never write to it or to anywhere around it.  You
1207 // could easily lock up your computer.
1208 //
1209 // Note that the DSP writes the position in little endian format; if you are
1210 // on a big endian machine, you will need to byte swap the postion
1211 // before you use it.
1212 //
1213 //===========================================================================
1214 
1215 ECHOSTATUS CEchoGals::GetAudioPositionPtr
1216 (
1217 	WORD		wPipeIndex,
1218 	PDWORD &	pdwPosition
1219 )
1220 {
1221 	if ( NULL == GetDspCommObject() || GetDspCommObject()->IsBoardBad() )
1222 		return ECHOSTATUS_DSP_DEAD;
1223 
1224 	if (wPipeIndex >= GetNumPipes())
1225 	{
1226 		pdwPosition = NULL;
1227 		return ECHOSTATUS_INVALID_CHANNEL;
1228 	}
1229 
1230 	PDWORD	pdwDspCommPos = GetDspCommObject()->GetAudioPositionPtr();
1231 
1232 	pdwPosition = pdwDspCommPos + wPipeIndex;
1233 
1234 	return ECHOSTATUS_OK;
1235 
1236 }	// ECHOSTATUS CEchoGals::GetAudioPositionPtr
1237 
1238