1 /********************************************************************************
2 ** Copyright (c) 1998-2000 Microsoft Corporation. All Rights Reserved.
3 **
4 ** Portions Copyright (c) 1998-1999 Intel Corporation
5 **
6 ********************************************************************************/
7
8 /* The file wavepcistream.cpp was reviewed by LCA in June 2011 and is acceptable for use by Microsoft. */
9
10 // Every debug output has "Modulname text"
11 #define STR_MODULENAME "AC97 Stream: "
12
13 #include "wavepciminiport.h"
14 #include "wavepcistream.h"
15
16 IMP_CMiniportStream_SetFormat(CMiniportWaveICHStream);
17 IMP_CMiniportStream_QueryInterface(CMiniportWaveICHStream, IMiniportWavePciStream);
18
19 /*****************************************************************************
20 * General Info
21 *****************************************************************************
22 * To protect the stBDList structure that is used to store mappings, we use a
23 * spin lock called MapLock. This spin lock is also acquired when we change
24 * the DMA registers. Normally, changes in stBDList and the DMA registers go
25 * hand in hand. In case we only want to change the DMA registers, we need
26 * to acquire the spin lock!
27 */
28
29 #ifdef _MSC_VER
30 #pragma code_seg("PAGE")
31 #endif
32 /*****************************************************************************
33 * CreateMiniportWaveICHStream
34 *****************************************************************************
35 * Creates a wave miniport stream object for the AC97 audio adapter. This is
36 * (nearly) like the macro STD_CREATE_BODY_ from STDUNK.H.
37 */
38 NTSTATUS CreateMiniportWaveICHStream
39 (
40 OUT CMiniportWaveICHStream **MiniportPCIStream,
41 IN PUNKNOWN pUnknownOuter,
42 _When_((PoolType & NonPagedPoolMustSucceed) != 0,
43 __drv_reportError("Must succeed pool allocations are forbidden. "
44 "Allocation failures cause a system crash"))
45 IN POOL_TYPE PoolType
46 )
47 {
48 PAGED_CODE ();
49
50 DOUT (DBG_PRINT, ("[CreateMiniportWaveICHStream]"));
51
52 //
53 // This is basically like the macro at stdunk with the change that we
54 // don't cast to interface unknown but to interface MiniportIchStream.
55 //
56 *MiniportPCIStream = new (PoolType, PoolTag)
57 CMiniportWaveICHStream (pUnknownOuter);
58 if (*MiniportPCIStream)
59 {
60 (*MiniportPCIStream)->AddRef ();
61 return STATUS_SUCCESS;
62 }
63
64 return STATUS_INSUFFICIENT_RESOURCES;
65 }
66
67
68 /*****************************************************************************
69 * CMiniportWaveICHStream::~CMiniportWaveICHStream
70 *****************************************************************************
71 * Destructor
72 */
~CMiniportWaveICHStream()73 CMiniportWaveICHStream::~CMiniportWaveICHStream ()
74 {
75 PAGED_CODE ();
76
77
78 DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::~CMiniportWaveICHStream]"));
79
80 //
81 // Print information about the scatter gather list.
82 //
83 DOUT (DBG_DMA, ("Head %d, Tail %d, Entries %d.",
84 stBDList.nHead, stBDList.nTail,
85 stBDList.nBDEntries));
86
87 // Release the scatter/gather table.
88 BDList_Free();
89 }
90
91
92 /*****************************************************************************
93 * CMiniportWaveICHStream::Init
94 *****************************************************************************
95 * This routine initializes the stream object, sets up the BDL, and programs
96 * the buffer descriptor list base address register for the pin being
97 * initialized.
98 */
Init_()99 NTSTATUS CMiniportWaveICHStream::Init_()
100 {
101 PAGED_CODE ();
102
103 DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::Init]"));
104
105 //
106 // The rule here is that we return when we fail without a cleanup.
107 // The destructor will relase the allocated memory.
108 //
109
110 //
111 // Initialize the BDL spinlock.
112 //
113 KeInitializeSpinLock (&MapLock);
114
115 //
116 // Setup the Buffer Descriptor List (BDL)
117 // Allocate 32 entries of 8 bytes (one BDL entry).
118 // The pointer is aligned on a 8 byte boundary (that's what we need).
119 //
120
121 if (!BDList_Alloc())
122 {
123 DOUT (DBG_ERROR, ("Failed AllocateCommonBuffer!"));
124 return STATUS_INSUFFICIENT_RESOURCES;
125 }
126
127 PPREFETCHOFFSET PreFetchOffset;
128 //
129 // Query for the new interface "PreFetchOffset" and use
130 // function offered there in case the interface is offered.
131 //
132 if (NT_SUCCESS(PortStream->QueryInterface(IID_IPreFetchOffset, (PVOID *)&PreFetchOffset)))
133 {
134 // why don't we pad by 32 sample frames
135 PreFetchOffset->SetPreFetchOffset(32 * (DataFormat->WaveFormatEx.nChannels * 2));
136 PreFetchOffset->Release();
137 }
138
139 return STATUS_SUCCESS;
140 }
141
142 /*****************************************************************************
143 * CMiniportWaveICHStream::GetAllocatorFraming
144 *****************************************************************************
145 * Returns the framing requirements for this device.
146 * That is sample size (for one sample) and preferred frame (buffer) size.
147 */
STDMETHODIMP_(NTSTATUS)148 STDMETHODIMP_(NTSTATUS) CMiniportWaveICHStream::GetAllocatorFraming
149 (
150 _Out_ PKSALLOCATOR_FRAMING AllocatorFraming
151 )
152 {
153 PAGED_CODE ();
154
155 ULONG SampleSize;
156
157 DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::GetAllocatorFraming]"));
158
159 //
160 // Determine sample size in bytes. Always number of
161 // channels * 2 (because 16-bit).
162 //
163 SampleSize = DataFormat->WaveFormatEx.nChannels * 2;
164
165 //
166 // Report the suggested requirements.
167 //
168 AllocatorFraming->RequirementsFlags =
169 KSALLOCATOR_REQUIREMENTF_SYSTEM_MEMORY |
170 KSALLOCATOR_REQUIREMENTF_PREFERENCES_ONLY;
171 AllocatorFraming->Frames = 8;
172
173 //
174 // Currently, arbitrarily selecting 10ms as the frame target size.
175 //
176 // This value needs to be sample block aligned for AC97 to work correctly.
177 // Assumes 100Hz minimum sample rate (otherwise FrameSize is 0 bytes)
178 //
179 AllocatorFraming->FrameSize = SampleSize * (DataFormat->WaveFormatEx.nSamplesPerSec / 100);
180 AllocatorFraming->FileAlignment = FILE_LONG_ALIGNMENT;
181 AllocatorFraming->PoolType = NonPagedPool;
182
183 return STATUS_SUCCESS;
184 }
185
186
187
188
189 /*****************************************************************************
190 * Non paged code begins here
191 *****************************************************************************
192 */
193
194 #ifdef _MSC_VER
195 #pragma code_seg()
196 #endif
197 /*****************************************************************************
198 * CMiniportWaveICHStream::PowerChangeNotify_
199 *****************************************************************************
200 * This functions saves and maintains the stream state through power changes.
201 */
PowerChangeNotify_(IN POWER_STATE NewState)202 void CMiniportWaveICHStream::PowerChangeNotify_
203 (
204 IN POWER_STATE NewState
205 )
206 {
207 KIRQL OldIrql;
208
209 KeAcquireSpinLock (&MapLock,&OldIrql);
210
211 if(NewState.DeviceState == PowerDeviceD0)
212 {
213 ResetDMA ();
214
215 // Restore the remaining DMA registers, that is last valid index
216 // only if the index is not pointing to 0. Note that the index is
217 // equal to head + entries.
218 if (stBDList.nTail)
219 {
220 WriteReg8 (X_LVI, (UCHAR)((stBDList.nTail - 1) & BDL_MASK));
221 }
222 }
223 else
224 {
225
226 // Disable interrupts and stop DMA just in case.
227 WriteReg8 (X_CR, (UCHAR)0);
228
229 // Get current index
230 // int nCurrentIndex = (int)ReadReg8 (X_CIV);
231 }
232
233 KeReleaseSpinLock (&MapLock,OldIrql);
234 }
235
236 /*****************************************************************************
237 * CMiniportWaveICHStream::SetState
238 *****************************************************************************
239 * This routine sets/changes the DMA engine state to play, stop, or pause
240 */
STDMETHODIMP_(NTSTATUS)241 STDMETHODIMP_(NTSTATUS) CMiniportWaveICHStream::SetState
242 (
243 _In_ KSSTATE State
244 )
245 {
246 KIRQL OldIrql;
247
248 DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::SetState]"));
249 DOUT (DBG_STREAM, ("SetState to %d", State));
250
251
252 //
253 // Start or stop the DMA engine dependent of the state.
254 //
255 switch (State)
256 {
257 case KSSTATE_STOP:
258 // acquire the mapping spin lock
259 KeAcquireSpinLock (&MapLock,&OldIrql);
260
261 // Just pause DMA. If we have mappings left in our queue,
262 // portcls will call RevokeMappings to destroy them.
263 PauseDMA ();
264
265 // release the mapping spin lock
266 KeReleaseSpinLock (&MapLock,OldIrql);
267
268 // Release processed mappings
269 ReleaseUsedMappings ();
270
271 // Reset the position counters.
272 TotalBytesMapped = TotalBytesReleased = 0;
273 break;
274
275 case KSSTATE_ACQUIRE:
276 case KSSTATE_PAUSE:
277 // acquire the mapping spin lock
278 KeAcquireSpinLock (&MapLock,&OldIrql);
279
280 // pause now.
281 PauseDMA ();
282
283 // release the mapping spin lock
284 KeReleaseSpinLock (&MapLock,OldIrql);
285
286 // Release processed mappings
287 ReleaseUsedMappings ();
288
289 break;
290
291 case KSSTATE_RUN:
292 //
293 // Let's rock.
294 //
295
296
297 // Make sure we are not running already.
298 if (DMAEngineState & DMA_ENGINE_ON)
299 {
300 return STATUS_SUCCESS;
301 }
302
303 // Release processed mappings.
304 ReleaseUsedMappings ();
305
306 // Get new mappings.
307 GetNewMappings ();
308
309 // acquire the mapping spin lock
310 KeAcquireSpinLock (&MapLock,&OldIrql);
311
312 // Kick DMA again just in case.
313 ResumeDMA (DMA_ENGINE_PEND);
314
315 // release the mapping spin lock
316 KeReleaseSpinLock (&MapLock,OldIrql);
317 break;
318 }
319
320 return STATUS_SUCCESS;
321 }
322
323 /*****************************************************************************
324 * CMiniportWaveICHStream::Service
325 *****************************************************************************
326 * This routine is called by the port driver in response to the interrupt
327 * service routine requesting service on the stream's service group.
328 * Requesting service on the service group results in a DPC being scheduled
329 * that calls this routine when it runs.
330 */
STDMETHODIMP_(void)331 STDMETHODIMP_(void) CMiniportWaveICHStream::Service (void)
332 {
333
334 DOUT (DBG_PRINT, ("Service"));
335
336 // release all mappings
337 ReleaseUsedMappings ();
338
339 // get new mappings
340 GetNewMappings ();
341 }
342
343
344 /*****************************************************************************
345 * CMiniportWaveICHStream::NormalizePhysicalPosition
346 *****************************************************************************
347 * Given a physical position based on the actual number of bytes transferred,
348 * this function converts the position to a time-based value of 100ns units.
349 */
STDMETHODIMP_(NTSTATUS)350 STDMETHODIMP_(NTSTATUS) CMiniportWaveICHStream::NormalizePhysicalPosition
351 (
352 _Inout_ PLONGLONG PhysicalPosition
353 )
354 {
355 ULONG SampleSize;
356
357 DOUT (DBG_PRINT, ("NormalizePhysicalPosition"));
358
359 //
360 // Determine the sample size in bytes
361 //
362 SampleSize = DataFormat->WaveFormatEx.nChannels * 2;
363
364 //
365 // Calculate the time in 100ns steps.
366 //
367 *PhysicalPosition = (_100NS_UNITS_PER_SECOND / SampleSize *
368 *PhysicalPosition) / CurrentRate;
369
370 return STATUS_SUCCESS;
371 }
372
373
374 /*****************************************************************************
375 * CMiniportWaveICHStream::GetPosition
376 *****************************************************************************
377 * Gets the stream position. This is a byte count of the current position of
378 * a stream running on a particular DMA engine. We must return a sample
379 * accurate count or the MiniportDrv32 wave drift tests (35.2 & 36.2) will fail.
380 *
381 * The position is the sum of three parts:
382 * 1) The total number of bytes in released buffers
383 * 2) The position in the current buffer.
384 * 3) The total number of bytes in played but not yet released buffers
385 */
STDMETHODIMP_(NTSTATUS)386 STDMETHODIMP_(NTSTATUS) CMiniportWaveICHStream::GetPosition
387 (
388 _Out_ PULONGLONG Position
389 )
390 {
391 KIRQL OldIrql;
392 UCHAR nCurrentIndex = 0;
393 DWORD buffPos;
394
395 ASSERT (Position);
396
397 //
398 // Acquire the mapping spin lock.
399 //
400 KeAcquireSpinLock (&MapLock, &OldIrql);
401
402 //
403 // Start with TotalBytesReleased (mappings released).
404 //
405 *Position = TotalBytesReleased;
406
407 //
408 // If we have entries in the list, we may have buffers that have not been
409 // released but have been at least partially played.
410 //
411 if (DMAEngineState != DMA_ENGINE_OFF)
412 {
413 nCurrentIndex = GetBuffPos(&buffPos);
414
415 //
416 // Add in our position in the current buffer
417 //
418 *Position += buffPos;
419
420 //
421 // Total any buffers that have been played and not released.
422 //
423 if (nCurrentIndex != ((stBDList.nHead -1) & BDL_MASK))
424 {
425 int i = stBDList.nHead;
426 while (i != nCurrentIndex)
427 {
428 *Position += (ULONGLONG)stBDList.pMapData[i].ulBufferLength;
429 i = (i + 1) & BDL_MASK;
430 }
431 }
432 }
433
434 DOUT (DBG_POSITION, ("[GetPosition] POS: %08x'%08x\n", (DWORD)(*Position >> 32), (DWORD)*Position));
435
436 //
437 // Release the mapping spin lock.
438 //
439 KeReleaseSpinLock (&MapLock, OldIrql);
440
441
442 return STATUS_SUCCESS;
443 }
444
445
446 /*****************************************************************************
447 * CMiniportWaveICHStream::RevokeMappings
448 *****************************************************************************
449 * This routine is used by the port to revoke mappings previously delivered
450 * to the miniport stream that have not yet been unmapped. This would
451 * typically be called in response to an I/O cancellation request.
452 */
STDMETHODIMP_(NTSTATUS)453 STDMETHODIMP_(NTSTATUS) CMiniportWaveICHStream::RevokeMappings
454 (
455 _In_ PVOID FirstTag,
456 _In_ PVOID LastTag,
457 _Out_ PULONG MappingsRevoked
458 )
459 {
460 ASSERT (MappingsRevoked);
461
462 KIRQL OldIrql;
463 ULONG ulOldDMAEngineState;
464
465 DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::RevokeMappings]"));
466
467 //
468 // print information about the scatter gather list.
469 //
470 DOUT (DBG_DMA, ("Head %d, Tail %d, Entries %d.",
471 stBDList.nHead, stBDList.nTail,
472 stBDList.nBDEntries));
473
474 //
475 // Start accessing the mappings.
476 //
477 KeAcquireSpinLock (&MapLock, &OldIrql);
478
479 //
480 // Save old DMA engine state.
481 //
482 ulOldDMAEngineState = DMAEngineState;
483
484 //
485 // First stop the DMA engine so it won't process the next buffer in the
486 // scatter gather list which might be one of the revoked buffers.
487 //
488 PauseDMA ();
489
490 //
491 // Mark items as revoked
492 //
493
494 for (intptr_t i = (intptr_t)FirstTag; i != (intptr_t)LastTag; i = (i + 1) & BDL_MASK)
495 {
496 if (stBDList.pMapData[i].ulState == 1)
497 {
498 stBDList.pMapData[i].ulState = 2;
499 BDList[i].wLength = 0;
500 BDList[i].wPolicyBits = 0;
501 *MappingsRevoked += 1;
502 }
503 }
504
505 //
506 // Just un-pause the DMA engine if it was running before
507 //
508 ResumeDMA(ulOldDMAEngineState);
509
510 //
511 // Release the mapping spin lock
512 //
513 KeReleaseSpinLock (&MapLock, OldIrql);
514
515
516 return STATUS_SUCCESS;
517 }
518
519
520 /*****************************************************************************
521 * CMiniportWaveICHStream::MappingAvailable
522 *****************************************************************************
523 * This routine is called by the port driver to notify the stream that there
524 * are new mappings available. Note that this is ONLY called after the stream
525 * has previously had a GetMapping() call fail due to lack of available
526 * mappings.
527 */
STDMETHODIMP_(void)528 STDMETHODIMP_(void) CMiniportWaveICHStream::MappingAvailable (void)
529 {
530 DOUT (DBG_PRINT, ("MappingAvailable"));
531
532 //
533 // Release processed mappings.
534 //
535 ReleaseUsedMappings ();
536
537 //
538 // Process the new mappings.
539 //
540 GetNewMappings ();
541 }
542
543
544 /*****************************************************************************
545 * CMiniportWaveICHStream::GetNewMappings
546 *****************************************************************************
547 * This routine is called when new mappings are available from the port driver.
548 * The routine places mappings into the input mapping queue. AC97 can handle up
549 * to 32 entries (descriptors). We program the DMA registers if we have at least
550 * one mapping in the queue. The mapping spin lock must be held when calling
551 * this routine.
552 */
GetNewMappings(void)553 NTSTATUS CMiniportWaveICHStream::GetNewMappings (void)
554 {
555 KIRQL OldIrql;
556
557 NTSTATUS ntStatus = STATUS_SUCCESS;
558 ULONG ulBytesMapped = 0;
559 int nInsertMappings = 0;
560 int nTail; // aut. variable
561
562 DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::GetNewMappings]"));
563
564 // acquire the mapping spin lock
565 KeAcquireSpinLock (&MapLock,&OldIrql);
566
567 // detect reentrance
568 if(m_inGetMapping) {
569 m_inGetMapping = 2;
570 KeReleaseSpinLock (&MapLock,OldIrql);
571 return STATUS_SUCCESS;
572 }
573
574 #if (DBG)
575 if (ReadReg16 (X_SR) & SR_CELV)
576 {
577 //
578 // We starve. :-(
579 //
580 DOUT (DBG_DMA, ("[GetNewMappings] We starved ... Head %d, Tail %d, Entries %d.",
581 stBDList.nHead, stBDList.nTail, stBDList.nBDEntries));
582 }
583 #endif
584
585 //
586 // Get available mappings up to the max of 31 that we can hold in the BDL.
587 //
588 while (stBDList.nBDEntries < (MAX_BDL_ENTRIES - 1))
589 {
590 //
591 // Get the information from the list.
592 //
593 ULONG Flags;
594 ULONG ulTag = stBDList.nTail;
595 ULONG ulBufferLength;
596 PHYSICAL_ADDRESS PhysAddr;
597 PVOID VirtAddr;
598
599
600 // Release the mapping spin lock
601 NEW_MAPPINGS_AVAILBLE_MAYBE:
602 m_inGetMapping = TRUE;
603 KeReleaseSpinLock (&MapLock,OldIrql);
604
605 //
606 // Try to get the mapping from the port.
607 // Here comes the problem: When calling GetMapping or ReleaseMapping we
608 // cannot hold our spin lock. So we need to buffer the return values and
609 // stick the information into the structure later when we have the spin
610 // lock acquired.
611 //
612 ntStatus = PortStream->GetMapping ((PVOID)ULongToPtr(ulTag),
613 (PPHYSICAL_ADDRESS)&PhysAddr,
614 &VirtAddr,
615 &ulBufferLength,
616 &Flags);
617
618 // Acquire the mapping spin lock
619 KeAcquireSpinLock (&MapLock,&OldIrql);
620
621 //
622 // Quit this loop when we run out of mappings.
623 //
624 if (!NT_SUCCESS (ntStatus))
625 {
626 if(m_inGetMapping == 2)
627 goto NEW_MAPPINGS_AVAILBLE_MAYBE;
628 break;
629 }
630
631 // Sanity check: The audio stack will not give you data
632 // that you cannot handle, but an application could use
633 // DirectKS and send bad buffer down.
634
635 // One mapping needs to be <0x1FFFF bytes for mono
636 // streams on the AC97.
637 if (ulBufferLength > 0x1FFFE)
638 {
639 // That is a little too long. That should never happen.
640 DOUT (DBG_ERROR, ("[GetNewMappings] Buffer length too long!"));
641 ulBufferLength = 0x1FFFE;
642 }
643
644 // The AC97 can only handle WORD aligned buffers.
645 if (PhysAddr.LowPart & 0x01)
646 {
647 // we cannot play that! Set the buffer length to 0 so
648 // that the HW will skip the buffer.
649 DOUT (DBG_WARNING, ("[GetNewMappings] Buffer address unaligned!"));
650 ulBufferLength = 0;
651 }
652
653 // The AC97 cannot handle unaligned mappings with respect
654 // to the frame size (eg. 42 bytes on 4ch playback).
655 if (ulBufferLength % NumberOfChannels)
656 {
657 // modify the length (don't play the rest of the bytes)
658 DOUT (DBG_WARNING, ("[GetNewMappings] Buffer length unaligned!"));
659 ulBufferLength -= ulBufferLength % NumberOfChannels;
660 }
661
662 //
663 // Save the mapping.
664 //
665 nTail = stBDList.nTail;
666 stBDList.pMapData[nTail].ulBufferLength = ulBufferLength;
667 stBDList.pMapData[nTail].ulState = 1;
668 ulBytesMapped += ulBufferLength;
669
670 //
671 // Fill in the BDL entry with pointer to physical address and length.
672 //
673 BDList[nTail].dwPtrToPhyAddress = PhysAddr.LowPart;
674 BDList[nTail].wLength = (WORD)(ulBufferLength >> 1);
675 BDList[nTail].wPolicyBits = BUP_SET;
676
677 //
678 // Generate an interrupt when portcls tells us to or roughly every 10ms.
679 //
680 if (Flags || (ulBytesMapped > (CurrentRate * NumberOfChannels * 2) / 100))
681 {
682 BDList[nTail].wPolicyBits |= IOC_ENABLE;
683 ulBytesMapped = 0;
684 }
685
686 //
687 // Take the new mapping into account.
688 //
689 stBDList.nTail = (stBDList.nTail + 1) & BDL_MASK;
690 stBDList.nBDEntries++;
691 TotalBytesMapped += (ULONGLONG)ulBufferLength;
692 nInsertMappings++;
693
694 //
695 // Set last valid index (LVI) register! We need to do this here to avoid inconsistency
696 // of the BDList with the HW. Note that we need to release spin locks every time
697 // we call into portcls, that means we can be interrupted by ReleaseUsedMappings.
698 //
699 WriteReg8 (X_LVI, (UCHAR)nTail);
700 }
701
702 //
703 // If there were processed mappings, print out some debug messages and eventually try to
704 // restart DMA engine.
705 //
706 if (nInsertMappings)
707 {
708 //
709 // Print debug information ...
710 //
711 DOUT (DBG_DMA, ("[GetNewMappings] Got %d mappings.", nInsertMappings));
712 DOUT (DBG_DMA, ("[GetNewMappings] Head %d, Tail %d, Entries %d.",
713 stBDList.nHead, stBDList.nTail, stBDList.nBDEntries));
714
715 if(stBDList.nBDEntries >= 2)
716 ResumeDMA (DMA_ENGINE_PAUSE);
717 }
718
719 // Release the mapping spin lock
720 m_inGetMapping = FALSE;
721 KeReleaseSpinLock (&MapLock,OldIrql);
722
723 return ntStatus;
724 }
725
726
727 /*****************************************************************************
728 * CMiniportWaveICHStream::ReleaseUsedMappings
729 *****************************************************************************
730 * This routine unmaps previously mapped memory that the hardware has
731 * completed processing on. This routine is typically called at DPC level
732 * from the stream deferred procedure call that results from a stream
733 * interrupt. The mapping spin lock must be held when calling this routine.
734 */
ReleaseUsedMappings(void)735 NTSTATUS CMiniportWaveICHStream::ReleaseUsedMappings (void)
736 {
737 KIRQL OldIrql;
738 int nMappingsReleased = 0;
739
740 DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::ReleaseUsedMappings]"));
741
742 // acquire the mapping spin lock
743 KeAcquireSpinLock (&MapLock,&OldIrql);
744
745 //
746 // Clean up everything to that index.
747 //
748 while (stBDList.nBDEntries)
749 {
750 //
751 // Get current index
752 //
753 int nCurrentIndex = (int)ReadReg8 (X_CIV);
754
755 //
756 // When CIV is == Head -1 we released all mappings.
757 //
758 if (nCurrentIndex == ((stBDList.nHead - 1) & BDL_MASK))
759 {
760 break;
761 }
762
763 //
764 // Check if CIV is between head and tail.
765 //
766 if (nCurrentIndex < stBDList.nHead)
767 {
768 //
769 // Check for CIV being outside range.
770 //
771 if ((nCurrentIndex + MAX_BDL_ENTRIES) >=
772 (stBDList.nHead + stBDList.nBDEntries))
773 {
774 DOUT (DBG_ERROR, ("[ReleaseUsedMappings] CIV out of range!"));
775 break;
776 }
777 }
778 else
779 {
780 //
781 // Check for CIV being outside range.
782 //
783 if (nCurrentIndex >= (stBDList.nHead + stBDList.nBDEntries))
784 {
785 DOUT (DBG_ERROR, ("[ReleaseUsedMappings] CIV out of range!"));
786 break;
787 }
788 }
789
790 //
791 // Check to see if we've released all the buffers.
792 //
793 if (stBDList.nHead == nCurrentIndex)
794 {
795 if (nCurrentIndex == ((stBDList.nTail - 1) & BDL_MASK))
796 {
797 //
798 // A special case is starvation or stop of stream, when the
799 // DMA engine finished playing the buffers, CVI is equal LVI
800 // and SR_CELV is set.
801 //
802 if (!(ReadReg16 (X_SR) & SR_CELV))
803 {
804 // It is still playing the last buffer.
805 break;
806 }
807
808 //
809 // In case the CVI=LVI bit is set, nBDEntries should be 1
810 //
811 if (stBDList.nBDEntries != 1)
812 {
813 DOUT (DBG_ERROR, ("[ReleaseUsedMappings] Inconsitency: Tail reached and Entries != 1."));
814 }
815 }
816 else
817 {
818 //
819 // Bail out. Current Index did not move.
820 //
821 break;
822 }
823 }
824
825 //
826 // Save the tag and remove the entry from the list.
827 //
828 ULONG ulTag = stBDList.nHead;
829 stBDList.nBDEntries--;
830 stBDList.nHead = (stBDList.nHead + 1) & BDL_MASK;
831 nMappingsReleased++;
832
833 // if entry has not been revoked
834 if(stBDList.pMapData[ulTag].ulState == 1)
835 {
836 TotalBytesReleased += (ULONGLONG)stBDList.pMapData[ulTag].ulBufferLength;
837
838 // Release the mapping spin lock
839 KeReleaseSpinLock (&MapLock,OldIrql);
840
841 //
842 // Release this entry.
843 //
844 PortStream->ReleaseMapping ((PVOID)ULongToPtr(ulTag));
845
846 // acquire the mapping spin lock
847 KeAcquireSpinLock (&MapLock,&OldIrql);
848 }
849
850 stBDList.pMapData[ulTag].ulState = 0;
851 }
852
853
854 // Print some debug information in case we released mappings.
855 if (nMappingsReleased)
856 {
857 //
858 // Print release information and return.
859 //
860 DOUT (DBG_DMA, ("[ReleaseUsedMappings] Head %d, Tail %d, Entries %d.",
861 stBDList.nHead, stBDList.nTail, stBDList.nBDEntries));
862 }
863
864 // Release the mapping spin lock
865 KeReleaseSpinLock (&MapLock,OldIrql);
866
867 return STATUS_SUCCESS;
868 }
869
870 /*****************************************************************************
871 * Non paged code begins here
872 *****************************************************************************
873 */
874
875 #ifdef _MSC_VER
876 #pragma code_seg()
877 #endif
878
InterruptServiceRoutine()879 void CMiniportWaveICHStream::InterruptServiceRoutine()
880 {
881 //
882 // Request DPC service for PCM out.
883 //
884 Miniport->Port->Notify (ServiceGroup);
885 }
886