1// ****************************************************************************
2//
3//		CDaffyDuck.CPP
4//
5//		Implementation file for the CDaffyDuck class.
6//		Set editor tabs to 3 for your viewing pleasure.
7//
8// This file is part of Echo Digital Audio's generic driver library.
9// Copyright Echo Digital Audio Corporation (c) 1998 - 2005
10// All rights reserved
11// www.echoaudio.com
12//
13// This library is free software; you can redistribute it and/or
14// modify it under the terms of the GNU Lesser General Public
15// License as published by the Free Software Foundation; either
16// version 2.1 of the License, or (at your option) any later version.
17//
18// This library is distributed in the hope that it will be useful,
19// but WITHOUT ANY WARRANTY; without even the implied warranty of
20// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21// Lesser General Public License for more details.
22//
23// You should have received a copy of the GNU Lesser General Public
24// License along with this library; if not, write to the Free Software
25// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26//
27//---------------------------------------------------------------------------
28//
29// The head pointer tracks the next free slot in the circular buffers
30// The tail pointer tracks the oldest mapping.
31//
32// Fixme add integrity checks for all functions
33//
34//****************************************************************************
35
36#include "CEchoGals.h"
37
38/****************************************************************************
39
40	Construction/destruction
41
42 ****************************************************************************/
43
44//===========================================================================
45//
46// Overload new & delete so memory for this object is allocated
47//	from non-paged memory.
48//
49//===========================================================================
50
51PVOID CDaffyDuck::operator new( size_t Size )
52{
53	PVOID 		pMemory;
54	ECHOSTATUS 	Status;
55
56	Status = OsAllocateNonPaged(Size,&pMemory);
57
58	if ( (ECHOSTATUS_OK != Status) || (NULL == pMemory ))
59	{
60		ECHO_DEBUGPRINTF(("CDaffyDuck::operator new - memory allocation failed\n"));
61
62		pMemory = NULL;
63	}
64	else
65	{
66		memset( pMemory, 0, Size );
67	}
68
69	return pMemory;
70
71}	// PVOID CDaffyDuck::operator new( size_t Size )
72
73
74VOID  CDaffyDuck::operator delete( PVOID pVoid )
75{
76
77	if ( ECHOSTATUS_OK != OsFreeNonPaged( pVoid ) )
78	{
79		ECHO_DEBUGPRINTF(("CDaffyDuck::operator delete memory free failed\n"));
80	}
81
82}	// VOID  CDaffyDuck::operator delete( PVOID pVoid )
83
84
85//===========================================================================
86//
87// Constructor
88//
89//===========================================================================
90
91CDaffyDuck::CDaffyDuck
92(
93	PCOsSupport 	pOsSupport
94)
95{
96	//
97	//	Stash stuff
98	//
99	m_pOsSupport = pOsSupport;
100
101}	// CDaffyDuck::CDaffyDuck()
102
103
104//===========================================================================
105//
106// Destructor
107//
108//===========================================================================
109
110CDaffyDuck::~CDaffyDuck()
111{
112
113	if (NULL != m_pDuckPage)
114		m_pOsSupport->FreePhysPageBlock( PAGE_SIZE, m_pDuckPage);
115
116}	// CDaffyDuck::~CDaffyDuck()
117
118
119
120
121/****************************************************************************
122
123	Setup and initialization
124
125 ****************************************************************************/
126
127//===========================================================================
128//
129// Reset - resets the mapping and duck entry circular buffers
130//
131//===========================================================================
132
133void CDaffyDuck::Reset()
134{
135	//
136	//	Zero stuff
137	//
138	OsZeroMemory(m_Mappings,sizeof(m_Mappings));
139
140	m_dwHead = 0;
141	m_dwTail = 0;
142	m_dwCount = 0;
143	m_ullLastEndPos = 0;
144
145	//
146	// Set all duck entries to "end of list" except for the last one
147	//
148	DWORD i;
149
150	for (i = 0; i < MAX_ENTRIES; i++)
151	{
152		m_DuckEntries[i].PhysAddr = 0;
153		m_DuckEntries[i].dwSize = 0xffffffff;
154	}
155
156	//
157	// Put the physical address of the duck at the end of
158	// the m_DuckEntries array so the DSP will wrap around
159	// to the start of the duck.
160	//
161
162	m_DuckEntries[MAX_ENTRIES].PhysAddr = SWAP( m_dwDuckEntriesPhys );
163	m_DuckEntries[MAX_ENTRIES].dwSize = 0;
164
165}	// Reset
166
167
168//===========================================================================
169//
170// ResetStartPos - Takes the current list and re-calculates the
171// DMA end position for each mapping, starting at DMA position zero.
172//
173//===========================================================================
174
175void CDaffyDuck::ResetStartPos()
176{
177	DWORD dwRemaining,dwIndex;
178
179	m_ullLastEndPos = 0L;
180
181	//
182	// Re-calculate the end positions
183	//
184	dwRemaining = m_dwCount;
185	dwIndex = m_dwTail;
186	while (0 != dwRemaining)
187	{
188		if (	( 0 != m_DuckEntries[ dwIndex ].dwSize) &&
189				( 0 != m_DuckEntries[ dwIndex ].PhysAddr ) )
190		{
191			m_Mappings[dwIndex].ullEndPos =
192				m_ullLastEndPos + SWAP( m_DuckEntries[ dwIndex ].dwSize );
193
194			m_ullLastEndPos = m_Mappings[ dwIndex ].ullEndPos;
195		}
196		else
197		{
198			m_Mappings[dwIndex].ullEndPos = m_ullLastEndPos;
199		}
200
201		dwIndex++;
202		dwIndex &= ENTRY_INDEX_MASK;
203
204		dwRemaining--;
205	}
206
207}	// ResetStartPos
208
209
210
211/****************************************************************************
212
213	Mapping management
214
215 ****************************************************************************/
216
217//===========================================================================
218//
219// AddMapping
220//
221// Take a mapping and add it to the circular buffer.
222//
223// Note that the m_DuckEntries array is read by the DSP; entries must
224// therefore be stored in little-endian format.
225//
226// The buffer pointed to by dwPhysAddr and dwBytes must be
227// physically contiguous.
228//
229//===========================================================================
230
231ECHOSTATUS CDaffyDuck::AddMapping
232(
233	DWORD			dwPhysAddr,
234	DWORD			dwBytes,
235	NUINT 		Tag,
236	DWORD			dwInterrupt,
237	DWORD			&dwNumFreeEntries
238)
239{
240#ifdef INTEGRITY_CHECK
241	CheckIntegrity();
242#endif
243
244	//
245	// There must always be at least one free entry for the "end of list"
246	// entry.  dwInterrupt may be non-zero, so make sure that there's enough
247	// room for two more entries
248	//
249	if ((MAX_ENTRIES - m_dwCount) < 3)
250	{
251		ECHO_DEBUGPRINTF(("AddMapping - duck is full\n"));
252		return ECHOSTATUS_DUCK_FULL;
253	}
254
255	//
256	//	At least two slots are available in the circular
257	// buffer, so it's OK to add either a regular mapping or
258	// a mapping with a double zero
259	//
260	m_DuckEntries[m_dwHead].PhysAddr = SWAP( dwPhysAddr );
261	m_DuckEntries[m_dwHead].dwSize 	= SWAP( dwBytes );
262
263	m_Mappings[m_dwHead].Tag			= Tag;
264	m_Mappings[m_dwHead].ullEndPos	= m_ullLastEndPos + dwBytes;
265
266	m_ullLastEndPos = m_Mappings[m_dwHead].ullEndPos;
267
268	//
269	// If the caller wants an interrupt after this mapping, then
270	// dwInterrupt will be non-zero
271	//
272	if (dwInterrupt)
273	{
274		DWORD dwNext;
275
276		//
277		// Put in the double zero so the DSP will
278		// generate an interrupt
279		//
280		dwNext = m_dwHead + 1;
281		dwNext &= ENTRY_INDEX_MASK;
282
283		m_DuckEntries[dwNext].PhysAddr 	= 0;	// no need to swap zero!
284		m_DuckEntries[dwNext].dwSize 		= 0;
285
286		m_Mappings[dwNext].ullEndPos = m_ullLastEndPos;
287
288		m_dwHead += 2;
289		m_dwCount += 2;
290	}
291	else
292	{
293		m_dwHead++;
294		m_dwCount++;
295	}
296
297	//
298	// Wrap the head index
299	//
300	m_dwHead &=	ENTRY_INDEX_MASK;
301
302	//
303	// Return value to the caller
304	//
305	dwNumFreeEntries = MAX_ENTRIES - m_dwCount;
306
307//#ifdef _DEBUG
308#if 0
309	DWORD hi,lo;
310
311	hi = (DWORD) (m_ullLastEndPos >> 32);
312	lo = (DWORD) (m_ullLastEndPos & 0xffffffffL);
313
314	ECHO_DEBUGPRINTF(("Added tag %ld, end pos 0x%08lx%08lx (int %ld)\n",Tag,hi,lo,dwInterrupt));
315
316#ifdef INTEGRITY_CHECK
317	CheckIntegrity();
318#endif
319
320	ECHO_DEBUGPRINTF(("Daffy duck count is %ld\n",m_dwCount));
321
322#endif
323
324	return ECHOSTATUS_OK;
325
326}	// AddMapping
327
328
329//===========================================================================
330//
331// AddDoubleZero
332//
333// Adds a double zero to the circular buffer to cause the DSP to generate an
334// IRQ.
335//
336//===========================================================================
337
338ECHOSTATUS CDaffyDuck::AddDoubleZero()
339{
340	//
341	// There must always be at least one free entry for the "end of list"
342	// entry.
343	//
344	if ((MAX_ENTRIES - m_dwCount) < 2)
345	{
346		ECHO_DEBUGPRINTF(("AddDoubleZero - duck is full\n"));
347		return ECHOSTATUS_DUCK_FULL;
348	}
349
350	//ECHO_DEBUGPRINTF(("CDaffyDuck::AddDoubleZero  m_dwCount %ld\n",m_dwCount));
351
352	//
353	// Add the double zero
354	//
355	m_DuckEntries[m_dwHead].PhysAddr 	= 0;
356	m_DuckEntries[m_dwHead].dwSize 		= 0;
357	m_Mappings[m_dwHead].ullEndPos		= m_ullLastEndPos;
358
359	//
360	// Housekeeping
361	//
362	m_dwHead++;
363	m_dwHead &=	ENTRY_INDEX_MASK;
364
365	m_dwCount++;
366
367	return ECHOSTATUS_OK;
368
369}	// AddDoubleZero
370
371
372//===========================================================================
373//
374//	Wrap
375//
376// Put a "next PLE" pointer at the end of the duck to make the DSP
377// wrap around to the start; this creates a circular buffer.
378//
379//===========================================================================
380
381void CDaffyDuck::Wrap()
382{
383	ECHO_ASSERT(m_dwCount != MAX_ENTRIES);
384
385	//
386	// Put in the address of the start of the duck entries
387	// and a count of zero; a count of zero tells the DSP
388	// "Go look again for a duck entry at this address"
389	//
390	m_DuckEntries[m_dwHead].PhysAddr		= SWAP( m_dwDuckEntriesPhys );
391	m_DuckEntries[m_dwHead].dwSize		= 0;
392
393	m_dwHead++;
394	m_dwHead &= ENTRY_INDEX_MASK;
395
396	m_dwCount++;
397
398	m_fWrapped = TRUE;
399
400}	// Wrap
401
402
403
404//===========================================================================
405//
406// ReleaseUsedMappings
407//
408// Find all the mapping that've been consumed and return a list of tags
409//
410// Return value is the number of tags written to the array
411//
412//===========================================================================
413
414DWORD CDaffyDuck::ReleaseUsedMappings
415(
416	ULONGLONG 	ullDmaPos,
417	NUINT 		*Tags,		// an array of tags
418	DWORD			dwMaxTags	// the max number of tags in the array
419)
420{
421	DWORD dwTempAddr,dwTempSize;
422	NUINT Tag;
423	DWORD dwTagsFree;
424
425	dwTagsFree = dwMaxTags;
426	while ( (0 != m_dwCount) && (0 != dwTagsFree) )
427	{
428		//
429		// Get the data from the tail
430		//
431		Tag = m_Mappings[m_dwTail].Tag;
432		dwTempAddr = SWAP( m_DuckEntries[m_dwTail].PhysAddr );
433		dwTempSize = SWAP( m_DuckEntries[m_dwTail].dwSize );
434
435		//
436		// Is this an audio data mapping?
437		//
438		if ( (0 != dwTempAddr) && (0 != dwTempSize) )
439		{
440			//
441			// Is this audio data mapping done?
442			//
443			if ( ullDmaPos < m_Mappings[m_dwTail].ullEndPos )
444				break;
445
446			//
447			// This one's done
448			//
449			*Tags = Tag;
450			Tags++;
451			dwTagsFree--;
452
453			EjectTail();
454		}
455		else
456		{
457			//
458			// Is this non-audio data mapping done?
459			//
460			if ( ullDmaPos <= m_Mappings[m_dwTail].ullEndPos )
461				break;
462
463			//
464			// Pop it
465			//
466			EjectTail();
467		}
468	}
469
470	//
471	// Return the number written
472	//
473	return dwMaxTags - dwTagsFree;
474
475}	// ReleaseUsedMappings
476
477
478//===========================================================================
479//
480// RevokeMappings
481//
482// Returns the number actually revoked
483//
484//===========================================================================
485
486DWORD CDaffyDuck::RevokeMappings(NUINT FirstTag,NUINT LastTag)
487{
488	NUINT	Offset;
489	DWORD	dwNumRevoked;
490
491	dwNumRevoked = 0;
492
493
494	//----------------------------------------------------------------------
495	//
496	// The tags may be 32 bit counters, so it is possible that they will
497	// wrap around (that is, dwLastTag may be less than dwFirstTag).  If the
498	// tags have wrapped, use an offset so the compares work correctly.
499	//
500	//----------------------------------------------------------------------
501
502	Offset = 0;
503	if (LastTag < FirstTag)
504	{
505		Offset = LastTag;
506
507		LastTag -= Offset;
508		FirstTag -= Offset;
509	}
510
511
512	//----------------------------------------------------------------------
513	//
514	// Go through the list and revoke stuff
515	//
516	//----------------------------------------------------------------------
517
518	DWORD dwCount;
519	DWORD dwCurrentIndex;
520	DWORD dwNextIndex;
521	NUINT AdjustedTag;
522
523	dwCurrentIndex = m_dwTail;
524	dwCount = m_dwCount;
525	while (dwCount != 0)
526	{
527		//
528		// Get info for this mapping
529		//
530		AdjustedTag = m_Mappings[dwCurrentIndex].Tag - Offset;
531
532		//
533		// Only check this mapping if it contains audio data
534		//
535		if (	(0 != m_DuckEntries[dwCurrentIndex].PhysAddr) &&
536				(0 != m_DuckEntries[dwCurrentIndex].dwSize) )
537		{
538			//
539			// See if the current mapping needs to be revoked
540			//
541			if ((FirstTag <= AdjustedTag) &&
542				 (AdjustedTag <= LastTag))
543			{
544				//
545				// Revoke this tag
546				//
547				dwNumRevoked++;
548
549				//
550				// Change this duck into a duck entry pointer; the DSP
551				// will see that the size is zero and re-fetch the duck entry
552				// from the address specified in PhysAddr.
553				//
554				dwNextIndex = dwCurrentIndex + 1;
555				dwNextIndex &= ENTRY_INDEX_MASK;
556
557				m_DuckEntries[dwCurrentIndex].PhysAddr =
558					m_dwDuckEntriesPhys + (dwNextIndex * sizeof(DUCKENTRY) );
559				m_DuckEntries[dwCurrentIndex].dwSize = 0;
560
561			}
562		}
563
564		dwCurrentIndex++;
565		dwCurrentIndex &= ENTRY_INDEX_MASK;
566
567		dwCount--;
568	}
569
570
571	//----------------------------------------------------------------------
572	//
573	// If any mappings were revoked, do various housekeeping tasks
574	//
575	//----------------------------------------------------------------------
576
577	if (0 != dwNumRevoked)
578	{
579		CleanUpTail();
580		ResetStartPos();
581	}
582
583	return dwNumRevoked;
584
585}	// RevokeMappings
586
587
588
589//===========================================================================
590//
591// CleanUpTail
592//
593// Removes any non-audio mappings from the tail of the list; stops
594// removing if it finds an audio mapping
595//
596//===========================================================================
597
598void CDaffyDuck::CleanUpTail()
599{
600	while (0 != m_dwCount)
601	{
602		//
603		// Quit the loop at the first audio data entry
604		//
605		if (	(0 != m_DuckEntries[ m_dwTail ].PhysAddr) &&
606				(0 != m_DuckEntries[ m_dwTail ].dwSize) )
607			break;
608
609		//
610		// Pop goes the weasel
611		//
612		EjectTail();
613	}
614
615}	// CleanUpTail
616
617
618
619
620//===========================================================================
621//
622// EjectTail
623//
624// Removes a single mapping from the tail of the list
625//
626//===========================================================================
627
628void CDaffyDuck::EjectTail()
629{
630#ifdef _DEBUG
631	if (0 == m_dwCount)
632	{
633		ECHO_DEBUGPRINTF(("EjectTail called with zero count!\n"));
634		ECHO_DEBUGBREAK();
635		return;
636	}
637#endif
638
639	//
640	//	Mark this entry with the "end of list" values
641	//
642	m_DuckEntries[ m_dwTail ].PhysAddr = 0;
643	m_DuckEntries[ m_dwTail ].dwSize = 0xffffffff;
644
645	//
646	// Move the tail forward and decrement the count
647	//
648	m_dwTail++;
649	m_dwTail &= ENTRY_INDEX_MASK;
650
651	m_dwCount--;
652
653} // EjectTail
654
655
656
657//===========================================================================
658//
659// Adjusts the duck so that DMA will start from a given position; useful
660// when resuming from pause
661//
662//===========================================================================
663
664void CDaffyDuck::AdjustStartPos(ULONGLONG ullPos)
665{
666	DWORD dwCount,dwIndex;
667	ULONGLONG ullMapStartPos;
668	DWORD dwPhysAddr;
669	DWORD dwSize;
670
671
672	dwCount = m_dwCount;
673	dwIndex = m_dwTail;
674	while (0 != dwCount)
675	{
676		//
677		// Check DMA pos
678		//
679		if (ullPos >= m_Mappings[dwIndex].ullEndPos)
680			break;
681
682		dwSize = SWAP(m_DuckEntries[dwIndex].dwSize);
683		ullMapStartPos = m_Mappings[dwIndex].ullEndPos - dwSize;
684		if (ullPos >= ullMapStartPos)
685		{
686			dwPhysAddr = SWAP(m_DuckEntries[dwIndex].PhysAddr);
687			if ( (0 != dwPhysAddr) && (0 != dwSize) )
688			{
689				DWORD dwDelta;
690
691				dwDelta = (DWORD) (m_Mappings[dwIndex].ullEndPos - ullPos);
692				dwPhysAddr += dwDelta;
693				dwSize -= dwDelta;
694
695				m_DuckEntries[dwIndex].PhysAddr = SWAP(dwPhysAddr);
696				m_DuckEntries[dwIndex].dwSize = SWAP(dwSize);
697				break;
698			}
699		}
700
701		dwCount--;
702		dwIndex++;
703		dwIndex &= ENTRY_INDEX_MASK;
704	}
705
706}
707
708
709//===========================================================================
710//
711// GetPhysStartAddr
712//
713// This returns the physical address of the start of the scatter-gather
714// list; used to tell the DSP where to start looking for duck entries.
715//
716//===========================================================================
717
718DWORD CDaffyDuck::GetPhysStartAddr()
719{
720	return m_dwDuckEntriesPhys + (m_dwTail * sizeof(DUCKENTRY));
721}
722
723
724//===========================================================================
725//
726// CheckIntegrity
727//
728// Debug code - makes sure that the buffer count, head, and tail all agree
729//
730//===========================================================================
731
732#ifdef INTEGRITY_CHECK
733
734void CDaffyDuck::CheckIntegrity()
735{
736	DWORD dwDiff,dwCount,dwTemp,dwSum;
737
738	dwDiff = m_dwHead - m_dwTail;
739	if (dwDiff > 0x80000000)
740		dwDiff += MAX_ENTRIES;
741
742	if ((0 == dwDiff) && (MAX_ENTRIES == m_dwCount))
743		return;
744
745	if (dwDiff != m_dwCount)
746	{
747		ECHO_DEBUGPRINTF(("CDaffyDuck integrity check fail!  m_dwHead %ld  m_dwTail %ld  "
748								"m_dwCount %ld  m_Mappings[m_dwHead].dwNumEntries %ld\n",
749								m_dwHead,m_dwTail,m_dwCount,m_Mappings[m_dwHead].dwNumEntries));
750		ECHO_DEBUGBREAK();
751	}
752
753	dwTemp = m_dwTail;
754	dwCount = m_dwCount;
755	dwSum = 0;
756	while (dwCount)
757	{
758		dwSum += m_Mappings[dwTemp].dwNumEntries;
759
760		dwCount--;
761		dwTemp++;
762		dwTemp &= ENTRY_INDEX_MASK;
763	}
764
765	if (dwSum != m_dwCount)
766	{
767		ECHO_DEBUGPRINTF(("CDaffyDuck integrity check fail!  dwSum %ld  m_dwCount %ld\n",
768								dwSum,m_dwCount));
769		ECHO_DEBUGBREAK();
770	}
771
772}	// CheckIntegrity
773
774#endif // INTEGRITY_CHECK
775
776
777VOID CDaffyDuck::DbgDump()
778{
779	ECHO_DEBUGPRINTF(("duck list starts at virt %p, phys %08x\n",m_DuckEntries,m_dwDuckEntriesPhys));
780	ECHO_DEBUGPRINTF(("count %d  head %d  tail %d\n",m_dwCount,m_dwHead,m_dwTail));
781	ECHO_DEBUGPRINTF(("Head phys %08x   tail phys %08x\n",
782				(m_dwHead * sizeof(DUCKENTRY)) + m_dwDuckEntriesPhys,
783				(m_dwTail * sizeof(DUCKENTRY)) + m_dwDuckEntriesPhys));
784
785	DWORD idx,count;
786
787	idx = m_dwTail;
788	count = m_dwCount;
789	while (count != 0)
790	{
791		ECHO_DEBUGPRINTF(("\t%08x :  %08x  %08x\n",(idx * sizeof(DUCKENTRY)) + m_dwDuckEntriesPhys,
792														m_DuckEntries[idx].dwSize,m_DuckEntries[idx].PhysAddr));
793		count--;
794		idx ++;
795		idx &= ENTRY_INDEX_MASK;
796	}
797}
798
799//===========================================================================
800//
801// This function is used to create a CDaffyDuck object to
802// manage a scatter-gather list for a newly opened pipe.  Call
803// this instead of using "new CDaffyDuck" directly.
804//
805//===========================================================================
806
807CDaffyDuck * CDaffyDuck::MakeDaffyDuck(COsSupport *pOsSupport)
808{
809	ECHOSTATUS 	Status = ECHOSTATUS_OK;
810	CDaffyDuck 	*pDuck;
811
812	pDuck = new CDaffyDuck(	pOsSupport );
813	if (NULL == pDuck)
814	{
815		ECHO_DEBUGPRINTF(("CDaffyDuck::CDaffyDuck - duck entry malloc failed\n"));
816		return NULL;
817	}
818
819	//
820	// Allocate the page for the duck entries
821	//
822	DWORD dwSegmentSize;
823	PHYS_ADDR PhysAddr;
824	PPAGE_BLOCK pPageBlock;
825
826	Status = pOsSupport->AllocPhysPageBlock( PAGE_SIZE, pPageBlock);
827	if (ECHOSTATUS_OK != Status)
828	{
829		ECHO_DEBUGPRINTF(("CDaffyDuck::CDaffyDuck - duck entry page block malloc failed\n"));
830		delete pDuck;
831		return NULL;
832	}
833
834	pDuck->m_pDuckPage = pPageBlock;
835
836	pDuck->m_DuckEntries = (DUCKENTRY *) pOsSupport->GetPageBlockVirtAddress( pPageBlock );
837	pOsSupport->GetPageBlockPhysSegment(pPageBlock,
838													0,
839													PhysAddr,
840													dwSegmentSize);
841
842	pDuck->m_dwDuckEntriesPhys = PhysAddr;
843
844	//
845	// Finish initializing
846	//
847	pDuck->Reset();
848
849	return pDuck;
850
851}	// MakeDaffyDuck
852
853
854
855// *** CDaffyDuck.cpp ***
856
857