ReactOS 0.4.16-dev-297-gc569aee
rtstream.cpp
Go to the documentation of this file.
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 */
38(
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 //
51 if (*RTStream)
52 {
53 (*RTStream)->AddRef ();
54 return STATUS_SUCCESS;
55 }
56
58}
59
60
61/*****************************************************************************
62 * CAC97MiniportWaveRTStream::~CAC97MiniportWaveRTStream
63 *****************************************************************************
64 * Destructor
65 */
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);
81 BDList = NULL;
82 }
83 if (BDList)
84 {
86 }
87
88 //
89 // Release the port stream.
90 //
91 if (PortStream)
92 {
93 PortStream->Release ();
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 */
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 //
134 MAX_BDL_ENTRIES * sizeof (tBDEntry), PoolTag);
135 if (!BDList)
136 {
137 DOUT (DBG_ERROR, ("Failed to allocate the BD list!"));
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;
153 BDListMdl = PortStream->AllocatePagesForMdl (high, PAGE_SIZE);
154 if (!BDListMdl)
155 {
156 DOUT (DBG_ERROR, ("Failed to allocate page for BD list!"));
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!"));
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 */
187STDMETHODIMP_(NTSTATUS) CAC97MiniportWaveRTStream::AllocateAudioBuffer
188(
190 _Out_ PMDL *userModeBuffer,
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 //
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))
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 */
287STDMETHODIMP_(VOID) CAC97MiniportWaveRTStream::FreeAudioBuffer
288(
289 PMDL Mdl,
290 ULONG Size
291)
292{
293 PAGED_CODE ();
294
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 */
311STDMETHODIMP_(void) CAC97MiniportWaveRTStream::GetHWLatency
312(
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 */
329STDMETHODIMP_(NTSTATUS) CAC97MiniportWaveRTStream::GetPositionRegister
330(
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 */
347STDMETHODIMP_(NTSTATUS) CAC97MiniportWaveRTStream::GetClockRegister
348(
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 */
383STDMETHODIMP_(NTSTATUS) CAC97MiniportWaveRTStream::GetPosition
384(
386)
387{
388 UCHAR nCurrentIndex = 0;
389 DWORD bufferPos;
390
392
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
418{
419 //
420 // Update the LVI so that we cycle around in the scatter gather list.
421 //
423}
424
425#endif // (NTDDI_VERSION >= NTDDI_VISTA)
#define PAGED_CODE()
unsigned char BOOLEAN
LONG NTSTATUS
Definition: precomp.h:26
#define STDMETHODIMP_(t)
Definition: basetyps.h:44
NTSTATUS Init(IN CAC97MiniportWaveRT *Miniport_, IN PPORTWAVERTSTREAM PortStream, IN ULONG Channel, IN BOOLEAN Capture, IN PKSDATAFORMAT DataFormat)
Definition: rtstream.cpp:106
WORD NumberOfChannels
Definition: stream.h:45
void UpdateLviCyclic()
Definition: stream.h:172
NTSTATUS Init(IN CMiniport *Miniport_, IN PUNKNOWN PortStream, IN WavePins Pin_, IN BOOLEAN Capture_, IN PKSDATAFORMAT DataFormat_, OUT PSERVICEGROUP *ServiceGroup_)
Definition: stream.cpp:65
int GetBuffPos(DWORD *buffPos)
Definition: stream.cpp:509
PPORTSTREAM_ PortStream
Definition: stream.h:48
ULONG DMAEngineState
Definition: stream.h:57
void InterruptServiceRoutine()
Definition: rtstream.cpp:417
#define NULL
Definition: types.h:112
#define DOUT(lvl, strings)
Definition: debug.h:82
const int DBG_WARNING
Definition: debug.h:18
const int DMA_ENGINE_OFF
Definition: stream.h:4
#define ExAllocatePoolWithTag(hernya, size, tag)
Definition: env_spec_w32.h:350
#define PAGE_SIZE
Definition: env_spec_w32.h:49
#define ExFreePool(addr)
Definition: env_spec_w32.h:352
#define NonPagedPool
Definition: env_spec_w32.h:307
unsigned long DWORD
Definition: ntddk_ex.h:95
unsigned short WORD
Definition: ntddk_ex.h:93
size_t bufferSize
GLsizeiptr size
Definition: glext.h:5919
const USHORT IOC_ENABLE
Definition: ichreg.h:103
#define ASSERT(a)
Definition: mode.c:44
unsigned int UINT
Definition: ndis.h:50
#define DBG_ERROR
Definition: nfs41_debug.h:78
#define _Use_decl_annotations_
Definition: no_sal2.h:92
#define _Out_
Definition: no_sal2.h:160
#define _In_
Definition: no_sal2.h:158
#define UNREFERENCED_PARAMETER(P)
Definition: ntbasedef.h:325
PHYSICAL_ADDRESS NTAPI MmGetPhysicalAddress(IN PVOID Address)
Definition: stubs.c:685
IPortWaveRTStream * PPORTWAVERTSTREAM
Definition: portcls.h:1847
NTSTATUS CreateAC97MiniportWaveRTStream(OUT CAC97MiniportWaveRTStream **RTStream)
Definition: rtstream.cpp:38
const int MAX_BDL_ENTRIES
Definition: rtstream.h:24
#define STATUS_SUCCESS
Definition: shellext.h:65
WORD wPolicyBits
Definition: stream.h:33
WORD wLength
Definition: stream.h:32
DWORD dwPtrToPhyAddress
Definition: stream.h:31
static COORD Position
Definition: mouse.c:34
#define MAXULONG
Definition: typedefs.h:251
#define IN
Definition: typedefs.h:39
uint32_t ULONG
Definition: typedefs.h:59
#define OUT
Definition: typedefs.h:40
#define STATUS_UNSUCCESSFUL
Definition: udferr_usr.h:132
#define STATUS_INSUFFICIENT_RESOURCES
Definition: udferr_usr.h:158
LONGLONG QuadPart
Definition: typedefs.h:114
ULONG LowPart
Definition: typedefs.h:106
_Must_inspect_result_ _In_ WDFDEVICE _In_ PWDF_DEVICE_PROPERTY_DATA _In_ DEVPROPTYPE _In_ ULONG Size
Definition: wdfdevice.h:4533
_In_ WDFDEVICE _In_ PVOID _In_opt_ PMDL Mdl
_Must_inspect_result_ _In_opt_ PWDF_OBJECT_ATTRIBUTES _In_ _Strict_type_match_ POOL_TYPE _In_opt_ ULONG PoolTag
Definition: wdfmemory.h:164
#define DBG_PRINT(ppi, ch, level)
Definition: win32kdebug.h:169
enum _MEMORY_CACHING_TYPE MEMORY_CACHING_TYPE
@ MmCached
Definition: mmtypes.h:130
unsigned char UCHAR
Definition: xmlstorage.h:181