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 rtstream.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 RT Stream: "
12
13 #include "rtminiport.h"
14 #include "rtstream.h"
15
16 #if (NTDDI_VERSION >= NTDDI_VISTA)
17
18 /*****************************************************************************
19 * General Info
20 *****************************************************************************
21 * To protect the stBDList structure that is used to store mappings, we use a
22 * spin lock called MapLock. This spin lock is also acquired when we change
23 * the DMA registers. Normally, changes in stBDList and the DMA registers go
24 * hand in hand. In case we only want to change the DMA registers, we need
25 * to acquire the spin lock!
26 */
27
28 #ifdef _MSC_VER
29 #pragma code_seg("PAGE")
30 #endif
31 /*****************************************************************************
32 * CreateAC97MiniportWaveRTStream
33 *****************************************************************************
34 * Creates a wave miniport stream object for the AC97 audio adapter. This is
35 * (nearly) like the macro STD_CREATE_BODY_ from STDUNK.H.
36 */
CreateAC97MiniportWaveRTStream(OUT CAC97MiniportWaveRTStream ** RTStream)37 NTSTATUS CreateAC97MiniportWaveRTStream
38 (
39 OUT CAC97MiniportWaveRTStream **RTStream
40 )
41 {
42 PAGED_CODE ();
43
44 DOUT (DBG_PRINT, ("[CreateAC97MiniportWaveRTStream]"));
45
46 //
47 // This is basically like the macro at stdunk with the change that we
48 // don't cast to interface unknown but to interface CAC97MiniportWaveRTStream.
49 //
50 *RTStream = new (NonPagedPool, PoolTag) CAC97MiniportWaveRTStream (NULL);
51 if (*RTStream)
52 {
53 (*RTStream)->AddRef ();
54 return STATUS_SUCCESS;
55 }
56
57 return STATUS_INSUFFICIENT_RESOURCES;
58 }
59
60
61 /*****************************************************************************
62 * CAC97MiniportWaveRTStream::~CAC97MiniportWaveRTStream
63 *****************************************************************************
64 * Destructor
65 */
~CAC97MiniportWaveRTStream()66 CAC97MiniportWaveRTStream::~CAC97MiniportWaveRTStream ()
67 {
68 PAGED_CODE ();
69
70
71 DOUT (DBG_PRINT, ("[CAC97MiniportWaveRTStream::~CAC97MiniportWaveRTStream]"));
72
73 //
74 // Delete the scatter gather list since it's not needed anymore
75 //
76 if (BDListMdl && BDList)
77 {
78 PortStream->UnmapAllocatedPages (BDList, BDListMdl);
79 PortStream->FreePagesFromMdl (BDListMdl);
80 BDListMdl = NULL;
81 BDList = NULL;
82 }
83 if (BDList)
84 {
85 ExFreePool (BDList);
86 }
87
88 //
89 // Release the port stream.
90 //
91 if (PortStream)
92 {
93 PortStream->Release ();
94 PortStream = NULL;
95 }
96 }
97
98
99 /*****************************************************************************
100 * CAC97MiniportWaveRTStream::Init
101 *****************************************************************************
102 * This routine initializes the stream object & allocates the BDL.
103 * It doesn't allocate the audio buffer or initialize the BDL.
104 */
Init(IN CAC97MiniportWaveRT * Miniport_,IN PPORTWAVERTSTREAM PortStream_,IN ULONG Channel_,IN BOOLEAN Capture_,IN PKSDATAFORMAT DataFormat_)105 NTSTATUS CAC97MiniportWaveRTStream::Init
106 (
107 IN CAC97MiniportWaveRT *Miniport_,
108 IN PPORTWAVERTSTREAM PortStream_,
109 IN ULONG Channel_,
110 IN BOOLEAN Capture_,
111 IN PKSDATAFORMAT DataFormat_
112 )
113 {
114 PAGED_CODE ();
115
116 DOUT (DBG_PRINT, ("[CAC97MiniportWaveRTStream::Init]"));
117
118 ASSERT (Miniport_);
119 ASSERT (PortStream_);
120 ASSERT (DataFormat_);
121
122 //
123 // The rule here is that we return when we fail without a cleanup.
124 // The destructor will relase the allocated memory.
125 //
126
127 //
128 // Allocate memory for the BDL.
129 // First try the least expensive way, which is to allocate it from the pool.
130 // If that fails (it's outside of the controller's address range which can
131 // happen on 64bit machines or PAE) then use portcls's AllocatePagesForMdl.
132 //
133 BDList = (tBDEntry *)ExAllocatePoolWithTag (NonPagedPool,
134 MAX_BDL_ENTRIES * sizeof (tBDEntry), PoolTag);
135 if (!BDList)
136 {
137 DOUT (DBG_ERROR, ("Failed to allocate the BD list!"));
138 return STATUS_INSUFFICIENT_RESOURCES;
139 }
140
141 //
142 // Check to see if our HW can access it.
143 // If the HW cannot see the memory, free it and use AllocatePagesForMdl
144 // which allocates always complete pages, so we have to waste some memory.
145 //
146 if (MmGetPhysicalAddress (BDList).HighPart != 0)
147 {
148 PHYSICAL_ADDRESS high;
149
150 high.HighPart = 0;
151 high.LowPart = MAXULONG;
152 ExFreePool (BDList);
153 BDListMdl = PortStream->AllocatePagesForMdl (high, PAGE_SIZE);
154 if (!BDListMdl)
155 {
156 DOUT (DBG_ERROR, ("Failed to allocate page for BD list!"));
157 return STATUS_INSUFFICIENT_RESOURCES;
158 }
159 BDList = (tBDEntry *)PortStream->MapAllocatedPages (BDListMdl, MmCached);
160 if (!BDList)
161 {
162 PortStream->FreePagesFromMdl (BDListMdl);
163 BDListMdl = NULL;
164 DOUT (DBG_ERROR, ("Failed to map the page for the BD list!"));
165 return STATUS_INSUFFICIENT_RESOURCES;
166 }
167 }
168
169
170 return CMiniportStream::Init(Miniport_,
171 Channel_,
172 Capture_,
173 DataFormat_,
174 NULL);
175 }
176
177
178 /*****************************************************************************
179 * CAC97MiniportWaveRTStream::AllocateAudioBuffer
180 *****************************************************************************
181 * This functions allocates an audio buffer of the size specified and maps
182 * it into the scatter gather table of the AC97 DMA engine.
183 * Once audio is played the driver only changes the last valid index to make
184 * the DMA cycle through this buffer over and over again.
185 * The buffer needs to be freed when the stream gets destroyed.
186 */
STDMETHODIMP_(NTSTATUS)187 STDMETHODIMP_(NTSTATUS) CAC97MiniportWaveRTStream::AllocateAudioBuffer
188 (
189 _In_ ULONG size,
190 _Out_ PMDL *userModeBuffer,
191 _Out_ ULONG *bufferSize,
192 _Out_ ULONG *bufferOffset,
193 _Out_ MEMORY_CACHING_TYPE *cacheType
194 )
195 {
196 PAGED_CODE ();
197
198 //
199 // Make sure complete samples fit into the buffer.
200 //
201 if( size <= size % (NumberOfChannels * 2) )
202 {
203 return STATUS_UNSUCCESSFUL;
204 }
205 size -= size % (NumberOfChannels * 2);
206
207 //
208 // Validate that we're going to actually allocate a real amount of memory.
209 //
210 if (0 == size)
211 {
212 DOUT (DBG_WARNING, ("Zero byte memory allocation attempted."));
213 return STATUS_UNSUCCESSFUL;
214 }
215
216 //
217 // Allocate the buffer.
218 // The AC97 has problems playing 6ch data on page breaks (a page is 4096 bytes
219 // and doesn't contain complete samples of 6ch 16bit audio data). We therefore
220 // allocate contiguous memory that fits complete 6ch 16bit samples, however,
221 // contiguous memory is a lot more expensive to get and you might not get it
222 // at all. Useing non-contiguous memory (AllocatePagesForMdl) is therefore much
223 // better if your HW does support it. It is highly recommended to build future
224 // HW so that it can map a variable amount of pages, that it can cycle through
225 // the scatter gather list automatically and that it handles that case where
226 // samples are "split" between 2 pages.
227 //
228 PHYSICAL_ADDRESS low;
229 PHYSICAL_ADDRESS high;
230
231 low.QuadPart = 0;
232 high.HighPart = 0, high.LowPart = MAXULONG;
233 PMDL audioBufferMdl = PortStream->AllocateContiguousPagesForMdl (low, high, size);
234
235 //
236 // Check if the allocation was successful.
237 //
238 if (!audioBufferMdl)
239 {
240 DOUT (DBG_WARNING, ("[AllocateAudioBuffer] Can not allocate RT buffer."));
241 return STATUS_UNSUCCESSFUL;
242 }
243
244 //
245 // We got our memory. Program the BDL (scatter gather list) now.
246 //
247 //
248 // Note that when you use AllocatePagesForMdl that you might get less memory
249 // back. In this case you need to check the byte count of the Mdl and continue
250 // with that size.
251 //
252
253 //
254 // Store the information for portcls so that the buffer can be mapped to
255 // the client.
256 //
257 *userModeBuffer = audioBufferMdl;
258 *bufferSize = size;
259 *bufferOffset = 0;
260 *cacheType = MmCached;
261
262 //
263 // Program the BDL
264 //
265 for (UINT loop = 0; loop < MAX_BDL_ENTRIES; loop++)
266 {
267 BDList[loop].dwPtrToPhyAddress = PortStream->GetPhysicalPageAddress (audioBufferMdl, 0).LowPart;
268 BDList[loop].wLength = (WORD)size/2;
269 if ((loop == MAX_BDL_ENTRIES / 2) || (loop == MAX_BDL_ENTRIES - 1))
270 BDList[loop].wPolicyBits = IOC_ENABLE;
271 else
272 BDList[loop].wPolicyBits = 0;
273 }
274
275 return STATUS_SUCCESS;
276 }
277
278 /*****************************************************************************
279 * CAC97MiniportWaveRTStream::FreeAudioBuffer
280 *****************************************************************************
281 * This functions frees the previously allocated audio buffer. We don't do
282 * anything special here. This callback is mainly in the case you would have
283 * to reprogram HW or do something fancy with it. In our case we just delete
284 * the audio buffer MDL and the scatter gather list we allocated.
285 */
286 _Use_decl_annotations_
STDMETHODIMP_(VOID)287 STDMETHODIMP_(VOID) CAC97MiniportWaveRTStream::FreeAudioBuffer
288 (
289 PMDL Mdl,
290 ULONG Size
291 )
292 {
293 PAGED_CODE ();
294
295 UNREFERENCED_PARAMETER(Size);
296
297 //
298 // Just delete the MDL that was allocated with AllocateContiguousPagesForMdl.
299 //
300 if (NULL != Mdl)
301 {
302 PortStream->FreePagesFromMdl (Mdl);
303 }
304 }
305
306 /*****************************************************************************
307 * CAC97MiniportWaveRTStream::GetHWLatency
308 *****************************************************************************
309 * Returns the HW latency of the controller + codec.
310 */
STDMETHODIMP_(void)311 STDMETHODIMP_(void) CAC97MiniportWaveRTStream::GetHWLatency
312 (
313 _Out_ PKSRTAUDIO_HWLATENCY hwLatency
314 )
315 {
316 PAGED_CODE ();
317
318 hwLatency->FifoSize = 32; // 32 bytes I think
319 hwLatency->ChipsetDelay = 0; // PCI
320 hwLatency->CodecDelay = 4; // Wild guess. Take maximum.
321 }
322
323 /*****************************************************************************
324 * CAC97MiniportWaveRTStream::GetPositionRegister
325 *****************************************************************************
326 * We can't support this property b/c we don't have a memory mapped position
327 * register.
328 */
STDMETHODIMP_(NTSTATUS)329 STDMETHODIMP_(NTSTATUS) CAC97MiniportWaveRTStream::GetPositionRegister
330 (
331 _Out_ PKSRTAUDIO_HWREGISTER hwRegister
332 )
333 {
334 PAGED_CODE ();
335
336 UNREFERENCED_PARAMETER(hwRegister);
337
338 return STATUS_UNSUCCESSFUL;
339 }
340
341 /*****************************************************************************
342 * CAC97MiniportWaveRTStream::GetClockRegister
343 *****************************************************************************
344 * We can't support this property b/c we don't have a memory mapped clock
345 * register.
346 */
STDMETHODIMP_(NTSTATUS)347 STDMETHODIMP_(NTSTATUS) CAC97MiniportWaveRTStream::GetClockRegister
348 (
349 _Out_ PKSRTAUDIO_HWREGISTER hwRegister
350 )
351 {
352 PAGED_CODE ();
353
354 UNREFERENCED_PARAMETER(hwRegister);
355
356 return STATUS_UNSUCCESSFUL;
357 }
358
359
360 /*****************************************************************************
361 * Non paged code begins here
362 *****************************************************************************
363 */
364
365 #ifdef _MSC_VER
366 #pragma code_seg()
367 #endif
368
369
370
371 /*****************************************************************************
372 * CAC97MiniportWaveRTStream::GetPosition
373 *****************************************************************************
374 * Gets the stream position. This is a byte count of the current position of
375 * a stream running on a particular DMA engine. We must return a sample
376 * accurate count or the WaveDrv32 wave drift tests (35.2 & 36.2) will fail.
377 *
378 * The position is the sum of three parts:
379 * 1) The total number of bytes in released buffers
380 * 2) The position in the current buffer.
381 * 3) The total number of bytes in played but not yet released buffers
382 */
STDMETHODIMP_(NTSTATUS)383 STDMETHODIMP_(NTSTATUS) CAC97MiniportWaveRTStream::GetPosition
384 (
385 _Out_ PKSAUDIO_POSITION Position
386 )
387 {
388 UCHAR nCurrentIndex = 0;
389 DWORD bufferPos;
390
391 ASSERT (Position);
392
393 if (DMAEngineState == DMA_ENGINE_OFF)
394 {
395 Position->PlayOffset = 0;
396 Position->WriteOffset = 0;
397 }
398 else
399 {
400 nCurrentIndex = GetBuffPos(&bufferPos);
401 Position->PlayOffset = bufferPos;
402 Position->WriteOffset = bufferPos + NumberOfChannels * 2 * 8;
403 }
404
405 return STATUS_SUCCESS;
406 }
407
408 +/*****************************************************************************
409 + * Non paged code begins here
410 + *****************************************************************************
411 + */
412
413 #ifdef _MSC_VER
414 #pragma code_seg()
415 #endif
416
417 void CMiniportWaveICHStream::InterruptServiceRoutine()
418 {
419 //
420 // Update the LVI so that we cycle around in the scatter gather list.
421 //
422 UpdateLviCyclic();
423 }
424
425 #endif // (NTDDI_VERSION >= NTDDI_VISTA)
426