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