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