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 */ 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 */ 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 */ 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 */ 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_ 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 */ 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 */ 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 */ 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 */ 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