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 // Copyright Echo Digital Audio Corporation (c) 1998 - 2002 9 // All rights reserved 10 // www.echoaudio.com 11 // 12 // Permission is hereby granted, free of charge, to any person obtaining a 13 // copy of this software and associated documentation files (the 14 // "Software"), to deal with the Software without restriction, including 15 // without limitation the rights to use, copy, modify, merge, publish, 16 // distribute, sublicense, and/or sell copies of the Software, and to 17 // permit persons to whom the Software is furnished to do so, subject to 18 // the following conditions: 19 // 20 // - Redistributions of source code must retain the above copyright 21 // notice, this list of conditions and the following disclaimers. 22 // 23 // - Redistributions in binary form must reproduce the above copyright 24 // notice, this list of conditions and the following disclaimers in the 25 // documentation and/or other materials provided with the distribution. 26 // 27 // - Neither the name of Echo Digital Audio, nor the names of its 28 // contributors may be used to endorse or promote products derived from 29 // this Software without specific prior written permission. 30 // 31 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 32 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 33 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 34 // IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR 35 // ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 36 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 37 // SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. 38 // 39 //--------------------------------------------------------------------------- 40 // 41 // The head pointer tracks the next free slot in the circular buffers 42 // The tail pointer tracks the oldest mapping. 43 // 44 //**************************************************************************** 45 46 #include "CEchoGals.h" 47 48 49 50 /**************************************************************************** 51 52 Construction/destruction 53 54 ****************************************************************************/ 55 56 //=========================================================================== 57 // 58 // Overload new & delete so memory for this object is allocated 59 // from non-paged memory. 60 // 61 //=========================================================================== 62 63 PVOID CDaffyDuck::operator new( size_t Size ) 64 { 65 PVOID pMemory; 66 ECHOSTATUS Status; 67 68 Status = OsAllocateNonPaged(Size,&pMemory); 69 70 if ( (ECHOSTATUS_OK != Status) || (NULL == pMemory )) 71 { 72 ECHO_DEBUGPRINTF(("CDaffyDuck::operator new - memory allocation failed\n")); 73 74 pMemory = NULL; 75 } 76 else 77 { 78 memset( pMemory, 0, Size ); 79 } 80 81 return pMemory; 82 83 } // PVOID CDaffyDuck::operator new( size_t Size ) 84 85 86 VOID CDaffyDuck::operator delete( PVOID pVoid ) 87 { 88 if ( ECHOSTATUS_OK != OsFreeNonPaged( pVoid ) ) 89 { 90 ECHO_DEBUGPRINTF(("CDaffyDuck::operator delete memory free failed\n")); 91 } 92 93 } // VOID CDaffyDuck::operator delete( PVOID pVoid ) 94 95 96 //=========================================================================== 97 // 98 // Constructor 99 // 100 //=========================================================================== 101 102 CDaffyDuck::CDaffyDuck 103 ( 104 PCOsSupport pOsSupport, 105 CDspCommObject *pDspCommObject, 106 WORD wPipeIndex 107 ) 108 { 109 // 110 // Allocate the page for the duck entries 111 // 112 if ( ECHOSTATUS_OK != pOsSupport->OsPageAllocate( 1, 113 (PVOID *) &m_DuckEntries, 114 &m_dwDuckEntriesPhys ) ) 115 { 116 ECHO_DEBUGPRINTF( ("CDaffyDuck: memory allocation failed\n") ); 117 return; 118 } 119 120 // 121 // Stash stuff 122 // 123 m_pOsSupport = pOsSupport; 124 m_pDspCommObject = pDspCommObject; 125 m_wPipeIndex = wPipeIndex; 126 127 // 128 // Put the physical address of the duck at the end of 129 // the m_DuckEntries array so the DSP will wrap around 130 // to the start of the duck. 131 // 132 m_DuckEntries[MAX_ENTRIES].PhysAddr = SWAP( m_dwDuckEntriesPhys ); 133 134 } // CDaffyDuck::CDaffyDuck() 135 136 137 //=========================================================================== 138 // 139 // Destructor 140 // 141 //=========================================================================== 142 143 CDaffyDuck::~CDaffyDuck() 144 { 145 146 m_pOsSupport->OsPageFree(1,m_DuckEntries,m_dwDuckEntriesPhys); 147 148 } // CDaffyDuck::~CDaffyDuck() 149 150 151 152 153 /**************************************************************************** 154 155 Setup and initialization 156 157 ****************************************************************************/ 158 159 //=========================================================================== 160 // 161 // InitCheck - returns ECHOSTATUS_OK if the duck created OK 162 // 163 //=========================================================================== 164 165 ECHOSTATUS CDaffyDuck::InitCheck() 166 { 167 if (NULL == m_DuckEntries) 168 return ECHOSTATUS_NO_MEM; 169 170 return ECHOSTATUS_OK; 171 172 } // InitCheck 173 174 175 //=========================================================================== 176 // 177 // Reset - resets the mapping and duck entry circular buffers 178 // 179 //=========================================================================== 180 181 void CDaffyDuck::Reset() 182 { 183 // 184 // Zero everything 185 // 186 OsZeroMemory(m_DuckEntries,4096); 187 OsZeroMemory(m_Mappings,sizeof(m_Mappings)); 188 189 m_dwHead = 0; 190 m_dwTail = 0; 191 m_dwCount = 0; 192 m_ullLastEndPos = 0; 193 194 // 195 // Put the physical address of the duck at the end of 196 // the m_DuckEntries array so the DSP will wrap around 197 // to the start of the duck. 198 // 199 m_DuckEntries[MAX_ENTRIES].PhysAddr = SWAP( m_dwDuckEntriesPhys ); 200 201 } // Reset 202 203 204 //=========================================================================== 205 // 206 // ResetStartPos - Takes the current list and re-calculates the 207 // DMA end position for each mapping, starting at DMA position zero. 208 // 209 //=========================================================================== 210 211 void CDaffyDuck::ResetStartPos() 212 { 213 DWORD dwRemaining,dwIndex,dwNumEntries; 214 215 m_ullLastEndPos = 0L; 216 217 // 218 // See if the mapping at the buffer tail has been consumed 219 // 220 dwRemaining = m_dwCount; 221 dwIndex = m_dwTail; 222 while (0 != dwRemaining) 223 { 224 m_Mappings[dwIndex].ullEndPos = 225 m_ullLastEndPos + SWAP( m_DuckEntries[dwIndex].dwSize ); 226 227 m_ullLastEndPos = m_Mappings[dwIndex].ullEndPos; 228 229 dwNumEntries = m_Mappings[m_dwTail].dwNumEntries; 230 231 ASSERT(dwRemaining >= dwNumEntries); 232 233 dwIndex += dwNumEntries; 234 if (dwIndex >= MAX_ENTRIES) 235 dwIndex -= MAX_ENTRIES; 236 237 dwRemaining -= dwNumEntries; 238 } 239 240 } // ResetStartPos 241 242 243 244 245 /**************************************************************************** 246 247 Mapping management 248 249 ****************************************************************************/ 250 251 //=========================================================================== 252 // 253 // AddMapping 254 // 255 // Take a mapping and add it to the circular buffer. 256 // 257 // Note that the m_DuckEntries array is read by the DSP; entries must 258 // therefore be stored in little-endian format. 259 // 260 // The buffer pointed to by dwPhysAddr and dwBytes must be 261 // physically contiguous. 262 // 263 //=========================================================================== 264 265 ECHOSTATUS CDaffyDuck::AddMapping 266 ( 267 DWORD dwPhysAddr, 268 DWORD dwBytes, 269 DWORD dwTag, 270 DWORD dwInterrupt, 271 DWORD &dwNumFreeEntries 272 ) 273 { 274 #ifdef INTEGRITY_CHECK 275 CheckIntegrity(); 276 #endif 277 278 // 279 // The caller should only be trying to do this if 280 // there are at least two free entries 281 // 282 ASSERT((MAX_ENTRIES - m_dwCount) >= 2); 283 284 // 285 // At least two slots are available in the circular 286 // buffer, so it's OK to add either a regular mapping or 287 // a mapping with a double zero 288 // 289 m_DuckEntries[m_dwHead].PhysAddr = SWAP( dwPhysAddr ); 290 m_DuckEntries[m_dwHead].dwSize = SWAP( dwBytes ); 291 292 m_Mappings[m_dwHead].dwTag = dwTag; 293 m_Mappings[m_dwHead].ullEndPos = m_ullLastEndPos + dwBytes; 294 295 m_ullLastEndPos = m_Mappings[m_dwHead].ullEndPos; 296 297 /* 298 ECHO_DEBUGPRINTF(("CDaffyDuck::AddMapping m_dwCount %ld pipe index %d end pos %I64x\n", 299 m_dwCount,m_wPipeIndex,m_ullLastEndPos)); 300 */ 301 302 // 303 // If the caller wants an interrupt after this mapping, then 304 // dwInterrupt will be non-zero 305 // 306 if (dwInterrupt) 307 { 308 DWORD dwNext; 309 310 // 311 // Interrupt wanted - need to add an extra slot with the double zero 312 // 313 m_Mappings[m_dwHead].dwNumEntries = 2; 314 315 // 316 // Put in the double zero so the DSP will 317 // generate an interrupt 318 // 319 dwNext = m_dwHead + 1; 320 if (dwNext >= MAX_ENTRIES) 321 dwNext -= MAX_ENTRIES; 322 323 m_DuckEntries[dwNext].PhysAddr = 0; // no need to swap zero! 324 m_DuckEntries[dwNext].dwSize = 0; 325 326 m_dwCount += 2; 327 } 328 else 329 { 330 m_Mappings[m_dwHead].dwNumEntries = 1; 331 332 m_dwCount++; 333 } 334 335 // 336 // Move the head to point to the next empty slot 337 // 338 m_dwHead += m_Mappings[m_dwHead].dwNumEntries; 339 if (m_dwHead >= MAX_ENTRIES) 340 m_dwHead -= MAX_ENTRIES; // circular buffer wrap 341 342 // 343 // Return value to the caller 344 // 345 dwNumFreeEntries = MAX_ENTRIES - m_dwCount; 346 347 // 348 // Tell the DSP about the new buffer if the 349 // interrupt flag is set 350 // 351 if (dwInterrupt != 0) 352 { 353 m_pDspCommObject->AddBuffer( m_wPipeIndex ); 354 } 355 356 357 //#ifdef _DEBUG 358 #if 0 359 DWORD hi,lo; 360 361 hi = (DWORD) (m_ullLastEndPos >> 32); 362 lo = (DWORD) (m_ullLastEndPos & 0xffffffffL); 363 364 ECHO_DEBUGPRINTF(("Added tag %ld, end pos 0x%08lx%08lx (int %ld)\n",dwTag,hi,lo,dwInterrupt)); 365 366 #ifdef INTEGRITY_CHECK 367 CheckIntegrity(); 368 #endif 369 370 ECHO_DEBUGPRINTF(("Daffy duck count is %ld\n",m_dwCount)); 371 372 #endif 373 374 return ECHOSTATUS_OK; 375 376 } // AddMapping 377 378 379 //=========================================================================== 380 // 381 // AddDoubleZero 382 // 383 // Adds a double zero to the circular buffer to cause the DSP to generate an 384 // IRQ. If the buffer is full -or- the most recent entry already has a 385 // double zero, nothing happens. 386 // 387 //=========================================================================== 388 389 void CDaffyDuck::AddDoubleZero() 390 { 391 // 392 // Skip if the circular buffer is full or empty 393 // 394 if ((MAX_ENTRIES == m_dwCount) || (0 == m_dwCount)) 395 return; 396 397 //ECHO_DEBUGPRINTF(("CDaffyDuck::AddDoubleZero m_dwCount %ld\n",m_dwCount)); 398 399 // 400 // Search back before the head and find the most recent entry 401 // 402 DWORD dwEntry; 403 #ifdef _DEBUG 404 DWORD dwCount = 0; 405 #endif 406 407 408 do 409 { 410 #ifdef _DEBUG 411 dwCount++; 412 if (dwCount > 2) 413 { 414 ECHO_DEBUGPRINTF(("CDaffyDuck::AddDoubleZero - shouldn't search back more " 415 "than two entries!\n")); 416 ECHO_DEBUGBREAK(); 417 } 418 #endif 419 420 dwEntry = m_dwHead - 1; 421 if (dwEntry >= 0x80000000) 422 dwEntry += MAX_ENTRIES; // circular buffer wrap 423 424 if (m_Mappings[dwEntry].dwNumEntries != 0) 425 break; 426 427 428 429 } while (dwEntry != m_dwTail); 430 431 // 432 // Quit if this entry already has a double zero 433 // 434 ASSERT(m_Mappings[dwEntry].dwNumEntries <= 2); 435 436 if (2 == m_Mappings[dwEntry].dwNumEntries) 437 return; 438 439 // 440 // Add the double zero 441 // 442 DWORD dwNext; 443 444 dwNext = dwEntry + 1; 445 if (dwNext >= MAX_ENTRIES) 446 dwNext -= MAX_ENTRIES; 447 448 m_DuckEntries[dwNext].PhysAddr = 0; 449 m_DuckEntries[dwNext].dwSize = 0; 450 451 m_Mappings[dwEntry].dwNumEntries += 1; 452 453 m_dwCount++; 454 455 m_dwHead = dwEntry + m_Mappings[dwEntry].dwNumEntries; 456 if (m_dwHead >= MAX_ENTRIES) 457 m_dwHead -= MAX_ENTRIES; 458 459 // 460 // Tell the DSP about it 461 // 462 m_pDspCommObject->AddBuffer( m_wPipeIndex ); 463 464 //ECHO_DEBUGPRINTF(("Added double zero\n")); 465 466 } // AddDoubleZero 467 468 469 //=========================================================================== 470 // 471 // Wrap 472 // 473 // Put a "next PLE" pointer at the end of the duck to make the DSP 474 // wrap around to the start; this creates a circular buffer. 475 // 476 //=========================================================================== 477 478 void CDaffyDuck::Wrap() 479 { 480 ASSERT(m_dwCount != MAX_ENTRIES); 481 482 // 483 // Put in the address of the start of the duck entries 484 // and a count of zero; a count of zero tells the DSP 485 // "Go look again for a duck entry at this address" 486 // 487 m_DuckEntries[m_dwHead].PhysAddr = SWAP( m_dwDuckEntriesPhys ); 488 m_DuckEntries[m_dwHead].dwSize = 0; 489 490 m_Mappings[m_dwHead].dwNumEntries = 1; 491 492 m_dwHead++; 493 if (m_dwHead >= MAX_ENTRIES) 494 m_dwHead -= MAX_ENTRIES; 495 496 m_dwCount++; 497 498 m_fWrapped = TRUE; 499 500 } // Wrap 501 502 503 //=========================================================================== 504 // 505 // ReleaseUsedMapping 506 // 507 // Find a mapping that's been consumed and return it 508 // 509 //=========================================================================== 510 511 ECHOSTATUS CDaffyDuck::ReleaseUsedMapping 512 ( 513 ULONGLONG ullDmaPos, 514 DWORD &dwTag 515 ) 516 { 517 518 #ifdef INTERGRITY_CHECK 519 CheckIntegrity(); 520 #endif 521 522 // 523 // See if there are any mappings and if the oldest mapping has 524 // been consumed by the DSP. 525 // 526 if ((0 != m_dwCount) && (ullDmaPos >= m_Mappings[m_dwTail].ullEndPos)) 527 { 528 DWORD dwNumEntries; 529 530 // 531 // Time to release the mapping at the tail 532 // 533 dwTag = m_Mappings[m_dwTail].dwTag; 534 535 // 536 // Remove the mapping from the circular buffer, 537 // taking dwNumEntries into consideration 538 // 539 dwNumEntries = m_Mappings[m_dwTail].dwNumEntries; 540 m_Mappings[m_dwTail].dwNumEntries = 0; 541 ASSERT(m_dwCount >= dwNumEntries); 542 543 m_dwTail += dwNumEntries; 544 if (m_dwTail >= MAX_ENTRIES) 545 m_dwTail -= MAX_ENTRIES; 546 547 m_dwCount -= dwNumEntries; 548 549 #ifdef INTEGRITY_CHECK 550 CheckIntegrity(); 551 #endif 552 553 #if 0 554 //#ifdef _DEBUG 555 ULONGLONG ullRemaining; 556 557 ullRemaining = m_ullLastEndPos - ullDmaPos; 558 559 ECHO_DEBUGPRINTF(("Released mapping for pipe index %d - bytes remaining %I64x\n", 560 m_wPipeIndex,ullRemaining)); 561 #endif 562 563 return ECHOSTATUS_OK; 564 } 565 566 // 567 // No bananas today, thanks. 568 // 569 return ECHOSTATUS_NO_DATA; 570 571 } // ReleaseUsedMappings 572 573 574 //=========================================================================== 575 // 576 // RevokeMappings 577 // 578 // Returns the number actually revoked 579 // 580 //=========================================================================== 581 582 DWORD CDaffyDuck::RevokeMappings(DWORD dwFirstTag,DWORD dwLastTag) 583 { 584 MAPPING *TempMappings = NULL; 585 DUCKENTRY *TempEntries = NULL; 586 DWORD dwNumTemp; 587 DWORD dwOffset; 588 DWORD dwNumRevoked = 0; 589 590 // 591 // Allocate the arrays 592 // 593 OsAllocateNonPaged( sizeof(MAPPING) * MAX_ENTRIES, // fixme make this part of the duck 594 (void **) &TempMappings ); 595 OsAllocateNonPaged( sizeof(DUCKENTRY) * MAX_ENTRIES, 596 (void **) &TempEntries ); 597 if ( (NULL == TempMappings) || 598 (NULL == TempEntries)) 599 { 600 goto exit; 601 } 602 603 // 604 // Tweak the end position so it's set OK 605 // when the duck is rebuilt 606 // 607 SetLastEndPos(GetStartPos()); 608 609 610 //---------------------------------------------------------------------- 611 // 612 // The tags are 32 bit counters, so it is possible that they will 613 // wrap around (that is, dwLastTag may be less than dwFirstTag). If the 614 // tags have wrapped, use an offset so the compares work correctly. 615 // 616 //---------------------------------------------------------------------- 617 618 dwOffset = 0; 619 if (dwLastTag < dwFirstTag) 620 { 621 dwOffset = 0x80000000; 622 623 dwLastTag -= dwOffset; 624 dwFirstTag -= dwOffset; 625 } 626 627 //---------------------------------------------------------------------- 628 // 629 // Empty out the duck into the temp arrays, throwing away 630 // the revoked mappings. 631 // 632 //---------------------------------------------------------------------- 633 634 ECHOSTATUS Status; 635 DWORD dwAdjustedTag; 636 637 dwNumTemp = 0; 638 do 639 { 640 Status = GetTail( TempEntries + dwNumTemp, 641 TempMappings + dwNumTemp); 642 if (ECHOSTATUS_OK == Status) 643 { 644 dwAdjustedTag = TempMappings[dwNumTemp].dwTag - dwOffset; 645 646 if ((dwFirstTag <= dwAdjustedTag) && 647 (dwAdjustedTag <= dwLastTag)) 648 { 649 // 650 // Revoke this tag 651 // 652 dwNumRevoked++; 653 } 654 else 655 { 656 // 657 // Save this tag 658 // 659 dwNumTemp++; 660 } 661 662 } 663 } while (ECHOSTATUS_OK == Status); 664 665 666 //---------------------------------------------------------------------- 667 // 668 // Add all the saved mappings back into this duck. 669 // 670 //---------------------------------------------------------------------- 671 672 DWORD i,dwDummy; 673 674 // 675 // Partially reset this duck 676 // 677 m_dwHead = 0; 678 m_dwTail = 0; 679 m_dwCount = 0; 680 681 // 682 // Put all the non-revoked mappings back 683 // 684 for (i = 0; i < dwNumTemp; i++) 685 { 686 AddMapping( TempEntries[i].PhysAddr, 687 TempEntries[i].dwSize, 688 TempMappings[i].dwTag, 689 TempMappings[i].dwNumEntries - 1, 690 dwDummy); 691 } 692 693 ECHO_DEBUGPRINTF(("CDaffyDuck::RevokeMappings for pipe index %d - m_dwHead %ld m_dwTail %ld m_dwCount %ld\n", 694 m_wPipeIndex,m_dwHead,m_dwTail,m_dwCount)); 695 696 exit: 697 if (TempEntries) OsFreeNonPaged(TempEntries); 698 if (TempMappings) OsFreeNonPaged(TempMappings); 699 700 return dwNumRevoked; 701 702 } // RevokeMappings 703 704 705 //=========================================================================== 706 // 707 // GetTail 708 // 709 // Unconditionally returns the mapping information from the tail of the 710 // circular buffer. 711 // 712 //=========================================================================== 713 714 ECHOSTATUS CDaffyDuck::GetTail(DUCKENTRY *pDuckEntry,MAPPING *pMapping) 715 { 716 // 717 // Punt if there are no entries in the circular buffer 718 // 719 if (0 == m_dwCount) 720 return ECHOSTATUS_NO_DATA; 721 722 // 723 // Copy the data 724 // 725 OsCopyMemory( pDuckEntry, 726 m_DuckEntries + m_dwTail, 727 sizeof(DUCKENTRY)); 728 OsCopyMemory( pMapping, 729 m_Mappings + m_dwTail, 730 sizeof(MAPPING)); 731 732 // 733 // Buffer housekeeping 734 // 735 DWORD dwNumEntries = m_Mappings[m_dwTail].dwNumEntries; 736 m_Mappings[m_dwTail].dwNumEntries = 0; 737 738 ASSERT(m_dwCount >= dwNumEntries); 739 740 m_dwTail += dwNumEntries; 741 if (m_dwTail >= MAX_ENTRIES) 742 m_dwTail -= MAX_ENTRIES; 743 744 m_dwCount -= dwNumEntries; 745 746 #ifdef INTEGRITY_CHECK 747 CheckIntegrity(); 748 #endif 749 750 return ECHOSTATUS_OK; 751 752 } // GetTail 753 754 755 //=========================================================================== 756 // 757 // CheckIntegrity 758 // 759 // Debug code - makes sure that the buffer count, head, and tail all agree 760 // 761 //=========================================================================== 762 763 #ifdef INTEGRITY_CHECK 764 765 void CDaffyDuck::CheckIntegrity() 766 { 767 DWORD dwDiff; 768 769 dwDiff = m_dwHead - m_dwTail; 770 if (dwDiff > 0x80000000) 771 dwDiff += MAX_ENTRIES; 772 773 if ((0 == dwDiff) && (MAX_ENTRIES == m_dwCount)) 774 return; 775 776 if (dwDiff != m_dwCount) 777 { 778 ECHO_DEBUGPRINTF(("CDaffyDuck integrity check fail! m_dwHead %ld m_dwTail %ld " 779 "m_dwCount %ld m_Mappings[m_dwHead].dwNumEntries %ld\n", 780 m_dwHead,m_dwTail,m_dwCount,m_Mappings[m_dwHead].dwNumEntries)); 781 ECHO_DEBUGBREAK(); 782 } 783 784 } // CheckIntegrity 785 786 #endif // INTEGRITY_CHECK 787 788 789 // *** CDaffyDuck.cpp *** 790