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