1 // Every debug output has "Modulname text"
2 #define STR_MODULENAME "AC97 Stream: "
3 
4 #include "shared.h"
5 #include "miniport.h"
6 
7 #ifdef _MSC_VER
8 #pragma code_seg("PAGE")
9 #endif
10 
11 
12 /*****************************************************************************
13  * CMiniportStream::NonDelegatingQueryInterface
14  *****************************************************************************
15  * Obtains an interface.  This function works just like a COM QueryInterface
16  * call and is used if the object is not being aggregated.
17  */
NonDelegatingQueryInterface(_In_ REFIID Interface,_COM_Outptr_ PVOID * Object,_In_ REFIID iStream,_In_ PUNKNOWN stream)18 NTSTATUS CMiniportStream::NonDelegatingQueryInterface
19 (
20     _In_         REFIID  Interface,
21     _COM_Outptr_ PVOID * Object,
22     _In_         REFIID iStream,
23     _In_         PUNKNOWN stream
24 )
25 {
26     PAGED_CODE ();
27 
28     ASSERT (Object);
29 
30     DOUT (DBG_PRINT, ("[CMiniportStream::NonDelegatingQueryInterface]"));
31 
32     //
33     // Convert for IID_IMiniportXXXStream
34     //
35     if (IsEqualGUIDAligned (Interface, iStream))
36     {
37         *Object = (PVOID)stream;
38     }
39     //
40     // Convert for IID_IDrmAudioStream
41     //
42     else if (IsEqualGUIDAligned (Interface, IID_IDrmAudioStream))
43     {
44         *Object = (PVOID)(PDRMAUDIOSTREAM)this;
45     }
46     //
47     // Convert for IID_IUnknown
48     //
49     else if (IsEqualGUIDAligned (Interface, IID_IUnknown))
50     {
51         *Object = (PVOID)stream;
52     }
53     else
54     {
55         *Object = NULL;
56         return STATUS_INVALID_PARAMETER;
57     }
58 
59     ((PUNKNOWN)*Object)->AddRef ();
60     return STATUS_SUCCESS;
61 }
62 
63 
Init(IN CMiniport * Miniport_,IN PUNKNOWN PortStream_,IN WavePins Pin_,IN BOOLEAN Capture_,IN PKSDATAFORMAT DataFormat_,OUT PSERVICEGROUP * ServiceGroup_)64 NTSTATUS CMiniportStream::Init
65 (
66     IN  CMiniport               *Miniport_,
67     IN  PUNKNOWN                PortStream_,
68     IN  WavePins                Pin_,
69     IN  BOOLEAN                 Capture_,
70     IN  PKSDATAFORMAT           DataFormat_,
71     OUT PSERVICEGROUP           *ServiceGroup_
72 )
73 {
74     PAGED_CODE ();
75 
76     DOUT (DBG_PRINT, ("[CMiniportStream::Init]"));
77 
78     ASSERT (Miniport_);
79     ASSERT (DataFormat_);
80 
81     //
82     // The rule here is that we return when we fail without a cleanup.
83     // The destructor will relase the allocated memory.
84     //
85     NTSTATUS ntStatus = STATUS_SUCCESS;
86 
87     //
88     // Save miniport pointer and addref it.
89     //
90     obj_AddRef(Miniport_, (PVOID *)&Miniport);
91 
92     //
93     // Save portstream interface pointer and addref it.
94     //
95     obj_AddRef(PortStream_, (PVOID *)&PortStream);
96 
97 
98     //
99     // Save channel ID and capture flag.
100     //
101     Pin = Pin_;
102     Capture = Capture_;
103 
104     //
105     // Save data format and current sample rate.
106     //
107     DataFormat = (PKSDATAFORMAT_WAVEFORMATEX)DataFormat_;
108     CurrentRate = DataFormat->WaveFormatEx.nSamplesPerSec;
109     NumberOfChannels = DataFormat->WaveFormatEx.nChannels;
110 
111 
112     if (ServiceGroup_)
113     {
114 
115         //
116         // Create a service group (a DPC abstraction/helper) to help with
117         // interrupts.
118         //
119         ntStatus = PcNewServiceGroup (&ServiceGroup, NULL);
120         if (!NT_SUCCESS (ntStatus))
121         {
122             DOUT (DBG_ERROR, ("Failed to create a service group!"));
123             return ntStatus;
124         }
125     }
126 
127     //
128     // Store the base address of this DMA engine.
129     //
130     if (Capture)
131     {
132         //
133         // could be PCM or MIC capture
134         //
135         if (Pin == PIN_WAVEIN)
136         {
137             // Base address for DMA registers.
138             m_ulBDAddr = PI_BDBAR;
139         }
140         else
141         {
142             // Base address for DMA registers.
143             m_ulBDAddr = MC_BDBAR;
144         }
145     }
146     else    // render
147     {
148         // Base address for DMA registers.
149         m_ulBDAddr = PO_BDBAR;
150     }
151 
152     //
153     // Reset the DMA and set the BD list pointer.
154     //
155     ResetDMA ();
156 
157     //
158     // Now set the requested sample rate. In case of a failure, the object
159     // gets destroyed and releases all memory etc.
160     //
161     ntStatus = SetFormat (DataFormat_);
162     if (!NT_SUCCESS (ntStatus))
163     {
164         DOUT (DBG_ERROR, ("Stream init SetFormat call failed!"));
165         return ntStatus;
166     }
167 
168     //
169     // Initialize the device state.
170     //
171     m_PowerState = PowerDeviceD0;
172 
173     //
174     // Call miniport specific init routine
175     //
176     ntStatus = Init_();
177     if (!NT_SUCCESS (ntStatus))
178     {
179         return ntStatus;
180     }
181 
182     //
183     // Setup the Buffer Descriptor Base Address (BDBA) register.
184     //
185     WriteReg32(0,  BDList_PhysAddr.LowPart);
186 
187     //
188     // Pass the ServiceGroup pointer to portcls.
189     //
190     obj_AddRef(ServiceGroup, (PVOID *)ServiceGroup_);
191 
192     //
193     // Store the stream pointer, it is used by the ISR.
194     //
195     Miniport->Streams[Pin/2] = this;
196 
197     return STATUS_SUCCESS;
198 }
199 
200 
~CMiniportStream()201 CMiniportStream::~CMiniportStream()
202 {
203     if (Miniport)
204     {
205         //
206         // Disable interrupts and stop DMA just in case.
207         //
208         if (Miniport->AdapterCommon)
209         {
210             WriteReg8 (X_CR, (UCHAR)0);
211 
212             //
213             // Update also the topology miniport if this was the render stream.
214             //
215             if (Miniport->AdapterCommon->GetMiniportTopology () &&
216                 (Pin == PIN_WAVEOUT))
217             {
218                 Miniport->AdapterCommon->GetMiniportTopology ()->SetCopyProtectFlag (FALSE);
219             }
220         }
221 
222         //
223         // Remove stream from miniport Streams array.
224         //
225         if (Miniport->Streams[Pin/2] == this)
226         {
227             Miniport->Streams[Pin/2] = NULL;
228         }
229     }
230 
231     obj_Release((PVOID *)&Miniport);
232     obj_Release((PVOID *)&ServiceGroup);
233     obj_Release((PVOID *)&PortStream);
234 }
235 
236 
237 /*****************************************************************************
238  * CMiniportStream::SetContentId
239  *****************************************************************************
240  * This routine gets called by drmk.sys to pass the content to the driver.
241  * The driver has to enforce the rights passed.
242  */
STDMETHODIMP_(NTSTATUS)243 STDMETHODIMP_(NTSTATUS) CMiniportStream::SetContentId
244 (
245     _In_  ULONG       contentId,
246     _In_  PCDRMRIGHTS drmRights
247 )
248 {
249     PAGED_CODE ();
250 
251     DOUT (DBG_PRINT, ("[CMiniportStream::SetContentId]"));
252 
253     UNREFERENCED_PARAMETER(contentId);
254 
255     //
256     // If "drmRights->DigitalOutputDisable" is set, we need to disable S/P-DIF.
257     // Currently, we don't have knowledge about the S/P-DIF interface. However,
258     // in case you expanded the driver with S/P-DIF features you need to disable
259     // S/P-DIF or fail SetContentId. If you have HW that has S/P-DIF turned on
260     // by default and you don't know how to turn off (or you cannot do that)
261     // then you must fail SetContentId.
262     //
263     // In our case, we assume the codec has no S/P-DIF or disabled S/P-DIF by
264     // default, so we can ignore the flag.
265     //
266     // Store the copyright flag. We have to disable PCM recording if it's set.
267     //
268     if (!Miniport->AdapterCommon->GetMiniportTopology ())
269     {
270         DOUT (DBG_ERROR, ("Topology pointer not set!"));
271         return STATUS_UNSUCCESSFUL;
272     }
273     else
274     {
275         Miniport->AdapterCommon->GetMiniportTopology ()->
276             SetCopyProtectFlag (drmRights->CopyProtect);
277     }
278 
279     //
280     // We assume that if we can enforce the rights, that the old content
281     // will be destroyed. We don't need to store the content id since we
282     // have only one playback channel, so we are finished here.
283     //
284 
285     return STATUS_SUCCESS;
286 }
287 
PowerChangeNotify(IN POWER_STATE NewState)288 void CMiniportStream::PowerChangeNotify
289 (
290     IN  POWER_STATE NewState
291 )
292 {
293     DOUT (DBG_PRINT, ("[CMiniportStream::PowerChangeNotify]"));
294 
295     //
296     // We don't have to check the power state, that's already done by the wave
297     // miniport.
298     //
299 
300     DOUT (DBG_POWER, ("Changing state to D%d.",
301                      (ULONG)NewState.DeviceState - (ULONG)PowerDeviceD0));
302 
303     switch (NewState.DeviceState)
304     {
305         case PowerDeviceD0:
306             //
307             // If we are coming from D2 or D3 we have to restore the registers cause
308             // there might have been a power loss.
309             //
310             if ((m_PowerState == PowerDeviceD3) || (m_PowerState == PowerDeviceD2))
311             {
312                 PowerChangeNotify_(NewState);
313             }
314             break;
315 
316         case PowerDeviceD1:
317             // Here we do nothing. The device has still enough power to keep all
318             // it's register values.
319             break;
320 
321         case PowerDeviceD2:
322         case PowerDeviceD3:
323             //
324             // If we power down to D2 or D3 we might loose power, so we have to be
325             // aware of the DMA engine resetting. In that case a play would start
326             // with scatter gather entry 0 (the current index is read only).
327             // This is fine with the RT port.
328             //
329 
330             PowerChangeNotify_(NewState);
331 
332             break;
333     }
334 
335     //
336     // Save the new state.  This local value is used to determine when to
337     // cache property accesses and when to permit the driver from accessing
338     // the hardware.
339     //
340     m_PowerState = NewState.DeviceState;
341     DOUT (DBG_POWER, ("Entering D%d",
342                       (ULONG)m_PowerState - (ULONG)PowerDeviceD0));
343 
344 }
345 
346 /*
347  *****************************************************************************
348  * This routine tests for proper data format (calls wave miniport) and sets
349  * or changes the stream data format.
350  * To figure out if the codec supports the sample rate, we just program the
351  * sample rate and read it back. If it matches we return happy, if not then
352  * we restore the sample rate and return unhappy.
353  * We fail this routine if we are currently running (playing or recording).
354  */
STDMETHODIMP_(NTSTATUS)355 STDMETHODIMP_(NTSTATUS) CMiniportStream::SetFormat
356 (
357     _In_  PKSDATAFORMAT   Format
358 )
359 {
360     PAGED_CODE ();
361 
362     ASSERT (Format);
363 
364     ULONG   TempRate;
365     DWORD   dwControlReg;
366 
367     DOUT (DBG_PRINT, ("[CMiniportStream::SetFormat]"));
368 
369     //
370     // Change sample rate when we are in the stop or pause states - not
371     // while running!
372     //
373     if (DMAEngineState == DMA_ENGINE_ON)
374     {
375         return STATUS_UNSUCCESSFUL;
376     }
377 
378     //
379     // Ensure format falls in proper range and is supported.
380     //
381     NTSTATUS ntStatus = Miniport->TestDataFormat (Format, Pin);
382     if (!NT_SUCCESS (ntStatus))
383         return ntStatus;
384 
385     //
386     // Retrieve wave format portion.
387     //
388     PWAVEFORMATPCMEX waveFormat = (PWAVEFORMATPCMEX)(Format + 1);
389 
390     //
391     // Save current rate in this context.
392     //
393     TempRate = waveFormat->Format.nSamplesPerSec;
394 
395     //
396     // Check if we have a codec with one sample rate converter and there are streams
397     // already open.
398     //
399     if (Miniport->Streams[PIN_WAVEIN_OFFSET] && Miniport->Streams[PIN_WAVEOUT_OFFSET] &&
400         !Miniport->AdapterCommon->GetNodeConfig (NODEC_PCM_VSR_INDEPENDENT_RATES))
401     {
402         //
403         // Figure out at which sample rate the other stream is running.
404         //
405         ULONG   ulFrequency;
406 
407         if (Miniport->Streams[PIN_WAVEIN_OFFSET] == this)
408             ulFrequency = Miniport->Streams[PIN_WAVEOUT_OFFSET]->CurrentRate;
409         else
410             ulFrequency = Miniport->Streams[PIN_WAVEIN_OFFSET]->CurrentRate;
411 
412         //
413         // Check if this sample rate is requested sample rate.
414         //
415         if (ulFrequency != TempRate)
416         {
417             return STATUS_UNSUCCESSFUL;
418         }
419     }
420 
421     //
422     // Program the AC97 to support n channels.
423     //
424     if (Pin == PIN_WAVEOUT)
425     {
426         dwControlReg = Miniport->AdapterCommon->ReadBMControlRegister32 (GLOB_CNT);
427         dwControlReg = (dwControlReg & 0x03F) |
428                        (((waveFormat->Format.nChannels >> 1) - 1) * GLOB_CNT_PCM4);
429         Miniport->AdapterCommon->WriteBMControlRegister (GLOB_CNT, dwControlReg);
430     }
431 
432     //
433     // Check for rate support by hardware.  If it is supported, then update
434     // hardware registers else return not implemented and audio stack will
435     // handle it.
436     //
437     if (Capture)
438     {
439         if (Pin == PIN_WAVEIN)
440         {
441             ntStatus = Miniport->AdapterCommon->
442                 ProgramSampleRate (AC97REG_RECORD_SAMPLERATE, TempRate);
443         }
444         else
445         {
446             ntStatus = Miniport->AdapterCommon->
447                 ProgramSampleRate (AC97REG_MIC_SAMPLERATE, TempRate);
448         }
449     }
450     else
451     {
452         //
453         // In the playback case we might need to update several DACs
454         // with the new sample rate.
455         //
456         ntStatus = Miniport->AdapterCommon->
457             ProgramSampleRate (AC97REG_FRONT_SAMPLERATE, TempRate);
458 
459         if (Miniport->AdapterCommon->GetNodeConfig (NODEC_SURROUND_DAC_PRESENT))
460         {
461             ntStatus = Miniport->AdapterCommon->
462                 ProgramSampleRate (AC97REG_SURROUND_SAMPLERATE, TempRate);
463         }
464         if (Miniport->AdapterCommon->GetNodeConfig (NODEC_LFE_DAC_PRESENT))
465         {
466             ntStatus = Miniport->AdapterCommon->
467                 ProgramSampleRate (AC97REG_LFE_SAMPLERATE, TempRate);
468         }
469     }
470 
471     if (NT_SUCCESS (ntStatus))
472     {
473         //
474         // print information and save the format information.
475         //
476         DataFormat = (PKSDATAFORMAT_WAVEFORMATEX)Format;
477         CurrentRate = TempRate;
478         NumberOfChannels = waveFormat->Format.nChannels;
479     }
480 
481     return ntStatus;
482 }
483 
484 /*****************************************************************************
485  * Non paged code begins here
486  *****************************************************************************
487  */
488 
489 #ifdef _MSC_VER
490 #pragma code_seg()
491 #endif
492 
UpdateDMA(void)493 UCHAR CMiniportStream::UpdateDMA (void)
494 {
495     // get X_CR register value
496     UCHAR RegisterValue = ReadReg8(X_CR);
497     UCHAR RegisterValueNew = RegisterValue & ~CR_RPBM;
498     if(DMAEngineState == DMA_ENGINE_ON)
499         RegisterValueNew |= CR_RPBM;
500 
501     // write X_CR register value
502     if(RegisterValue != RegisterValueNew)
503         WriteReg8(X_CR, RegisterValueNew);
504     return RegisterValueNew;
505 }
506 
507 
GetBuffPos(DWORD * buffPos)508 int CMiniportStream::GetBuffPos
509 (
510     DWORD* buffPos
511 )
512 {
513     int nCurrentIndex;
514     DWORD RegisterX_PICB;
515 
516     if (DMAEngineState == DMA_ENGINE_OFF)
517     {
518         *buffPos = 0;
519         return 0;
520     }
521 
522     //
523     // Repeat this until we get the same reading twice.  This will prevent
524     // jumps when we are near the end of the buffer.
525     //
526     do
527     {
528         nCurrentIndex = ReadReg8(X_CIV);
529 
530         RegisterX_PICB = ReadReg16 (X_PICB);
531     } while (nCurrentIndex != (int)ReadReg8(X_CIV));
532 
533     *buffPos = (BDList[nCurrentIndex].wLength - RegisterX_PICB) * 2;
534     return nCurrentIndex;
535 }
536 
537 /*****************************************************************************
538  * CMiniportStream::ResetDMA
539  *****************************************************************************
540  * This routine resets the Run/Pause bit in the control register. In addition, it
541  * resets all DMA registers contents.
542 
543  */
ResetDMA(void)544 void CMiniportStream::ResetDMA (void)
545 {
546     DOUT (DBG_PRINT, ("ResetDMA"));
547 
548     //
549     // Turn off DMA engine (or make sure it's turned off)
550     //
551     DMAEngineState = DMA_ENGINE_OFF;
552     UCHAR RegisterValue = UpdateDMA();
553 
554     //
555     // Reset all register contents.
556     //
557     RegisterValue |= CR_RR;
558     WriteReg8(X_CR, RegisterValue);
559 
560     //
561     // Wait until reset condition is cleared by HW; should not take long.
562     //
563     ULONG count = 0;
564     BOOL bTimedOut = TRUE;
565     do
566     {
567         if (!(ReadReg8(X_CR) & CR_RR))
568         {
569             bTimedOut = FALSE;
570             break;
571         }
572         KeStallExecutionProcessor (1);
573     } while (count++ < 10);
574 
575     if (bTimedOut)
576     {
577         DOUT (DBG_ERROR, ("ResetDMA TIMEOUT!!"));
578     }
579 
580     //
581     // We only want interrupts upon completion.
582     //
583     RegisterValue = CR_IOCE | CR_LVBIE;
584     WriteReg8(X_CR,  RegisterValue);
585 
586     //
587     // Setup the Buffer Descriptor Base Address (BDBA) register.
588     //
589     WriteReg32(0,  BDList_PhysAddr.LowPart);
590 }
591 
592 /*****************************************************************************
593  * CMiniportStream::ResumeDMA
594  *****************************************************************************
595  * This routine sets the Run/Pause bit for the particular DMA engine to resume
596  * it after it's been paused. This assumes that DMA registers content have
597  * been preserved.
598  */
ResumeDMA(ULONG state)599 void CMiniportStream::ResumeDMA (ULONG state)
600 {
601     DOUT (DBG_PRINT, ("ResumeDMA"));
602 
603     DMAEngineState |= state;
604     UpdateDMA();
605 }
606 
607 /*****************************************************************************
608  * CMiniportStream::PauseDMA
609  *****************************************************************************
610  * This routine pauses a hardware stream by reseting the Run/Pause bit in the
611  * control registers, leaving DMA registers content intact so that the stream
612  * can later be resumed.
613  */
PauseDMA(void)614 void CMiniportStream::PauseDMA (void)
615 {
616     DOUT (DBG_PRINT, ("PauseDMA"));
617 
618     DMAEngineState &= DMA_ENGINE_PAUSE;
619     UpdateDMA();
620 }
621 
622 
WriteReg8(ULONG addr,UCHAR data)623 void CMiniportStream::WriteReg8(ULONG addr, UCHAR data) { Miniport->AdapterCommon->
624     WriteBMControlRegister (m_ulBDAddr + addr, data); }
WriteReg16(ULONG addr,USHORT data)625 void CMiniportStream::WriteReg16(ULONG addr, USHORT data) { Miniport->AdapterCommon->
626     WriteBMControlRegister (m_ulBDAddr + addr, data); }
WriteReg32(ULONG addr,ULONG data)627 void CMiniportStream::WriteReg32(ULONG addr, ULONG data) { Miniport->AdapterCommon->
628     WriteBMControlRegister (m_ulBDAddr + addr, data); }
ReadReg8(ULONG addr)629 UCHAR CMiniportStream::ReadReg8(ULONG addr) { return Miniport->AdapterCommon->
630     ReadBMControlRegister8 (m_ulBDAddr + addr); }
ReadReg16(ULONG addr)631 USHORT CMiniportStream::ReadReg16(ULONG addr) { return Miniport->AdapterCommon->
632     ReadBMControlRegister16 (m_ulBDAddr + addr); }
ReadReg32(ULONG addr)633 ULONG CMiniportStream::ReadReg32(ULONG addr) { return Miniport->AdapterCommon->
634     ReadBMControlRegister32 (m_ulBDAddr + addr); }
635