Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1871 lines
57 KiB

  1. /********************************************************************************
  2. ** Copyright (c) 1998-2000 Microsoft Corporation. All Rights Reserved.
  3. **
  4. ** Portions Copyright (c) 1998-1999 Intel Corporation
  5. **
  6. ********************************************************************************/
  7. // Every debug output has "Modulname text"
  8. static char STR_MODULENAME[] = "ICH Stream: ";
  9. #include "minwave.h"
  10. #include "ichwave.h"
  11. /*****************************************************************************
  12. * General Info
  13. *****************************************************************************
  14. * To protect the stBDList structure that is used to store mappings, we use a
  15. * spin lock called MapLock. This spin lock is also acquired when we change
  16. * the DMA registers. Normally, changes in stBDList and the DMA registers go
  17. * hand in hand. In case we only want to change the DMA registers, we need
  18. * to acquire the spin lock!
  19. */
  20. #pragma code_seg("PAGE")
  21. /*****************************************************************************
  22. * CreateMiniportWaveICHStream
  23. *****************************************************************************
  24. * Creates a wave miniport stream object for the ICH audio adapter. This is
  25. * (nearly) like the macro STD_CREATE_BODY_ from STDUNK.H.
  26. */
  27. NTSTATUS CreateMiniportWaveICHStream
  28. (
  29. OUT CMiniportWaveICHStream **WaveIchStream,
  30. IN PUNKNOWN pUnknownOuter,
  31. IN POOL_TYPE PoolType
  32. )
  33. {
  34. PAGED_CODE ();
  35. DOUT (DBG_PRINT, ("[CreateMiniportWaveICHStream]"));
  36. //
  37. // This is basically like the macro at stdunk with the change that we
  38. // don't cast to interface unknown but to interface WaveIchStream.
  39. //
  40. *WaveIchStream = new (PoolType, 'rCcP')
  41. CMiniportWaveICHStream (pUnknownOuter);
  42. if (*WaveIchStream)
  43. {
  44. (*WaveIchStream)->AddRef ();
  45. return STATUS_SUCCESS;
  46. }
  47. return STATUS_INSUFFICIENT_RESOURCES;
  48. }
  49. /*****************************************************************************
  50. * CMiniportWaveICHStream::~CMiniportWaveICHStream
  51. *****************************************************************************
  52. * Destructor
  53. */
  54. CMiniportWaveICHStream::~CMiniportWaveICHStream ()
  55. {
  56. PAGED_CODE ();
  57. DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::~CMiniportWaveICHStream]"));
  58. //
  59. // Print information about the scatter gather list.
  60. //
  61. DOUT (DBG_DMA, ("Head %d, Tail %d, Tag counter %d, Entries %d.",
  62. stBDList.nHead, stBDList.nTail, stBDList.ulTagCounter,
  63. stBDList.nBDEntries));
  64. if (Wave)
  65. {
  66. //
  67. // Disable interrupts and stop DMA just in case.
  68. //
  69. if (Wave->AdapterCommon)
  70. {
  71. Wave->AdapterCommon->WriteBMControlRegister (m_ulBDAddr + X_CR, (UCHAR)0);
  72. //
  73. // Update also the topology miniport if this was the render stream.
  74. //
  75. if (Wave->AdapterCommon->GetMiniportTopology () &&
  76. (Channel == PIN_WAVEOUT_OFFSET))
  77. {
  78. Wave->AdapterCommon->GetMiniportTopology ()->SetCopyProtectFlag (FALSE);
  79. }
  80. }
  81. //
  82. // Remove stream from miniport Streams array.
  83. //
  84. if (Wave->Streams[Channel] == this)
  85. {
  86. Wave->Streams[Channel] = NULL;
  87. }
  88. //
  89. // Release the scatter/gather table.
  90. //
  91. if (stBDList.pBDEntry)
  92. {
  93. HalFreeCommonBuffer (Wave->AdapterObject,
  94. PAGE_SIZE,
  95. stBDList.PhysAddr,
  96. (PVOID)stBDList.pBDEntry,
  97. FALSE);
  98. stBDList.pBDEntry = NULL;
  99. }
  100. //
  101. // Release the miniport.
  102. //
  103. Wave->Release ();
  104. Wave = NULL;
  105. }
  106. //
  107. // Release the service group.
  108. //
  109. if (ServiceGroup)
  110. {
  111. ServiceGroup->Release ();
  112. ServiceGroup = NULL;
  113. }
  114. //
  115. // Release the mapping table.
  116. //
  117. if (stBDList.pMapData)
  118. {
  119. ExFreePool (stBDList.pMapData);
  120. stBDList.pMapData = NULL;
  121. }
  122. //
  123. // Release the port stream.
  124. //
  125. if (PortStream)
  126. {
  127. PortStream->Release ();
  128. PortStream = NULL;
  129. }
  130. }
  131. /*****************************************************************************
  132. * CMiniportWaveICHStream::Init
  133. *****************************************************************************
  134. * This routine initializes the stream object, sets up the BDL, and programs
  135. * the buffer descriptor list base address register for the pin being
  136. * initialized.
  137. */
  138. NTSTATUS CMiniportWaveICHStream::Init
  139. (
  140. IN CMiniportWaveICH *Miniport_,
  141. IN PPORTWAVEPCISTREAM PortStream_,
  142. IN ULONG Channel_,
  143. IN BOOLEAN Capture_,
  144. IN PKSDATAFORMAT DataFormat_,
  145. OUT PSERVICEGROUP *ServiceGroup_
  146. )
  147. {
  148. PAGED_CODE ();
  149. DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::Init]"));
  150. ASSERT (Miniport_);
  151. ASSERT (PortStream_);
  152. ASSERT (DataFormat_);
  153. ASSERT (ServiceGroup_);
  154. //
  155. // The rule here is that we return when we fail without a cleanup.
  156. // The destructor will relase the allocated memory.
  157. //
  158. NTSTATUS ntStatus = STATUS_SUCCESS;
  159. //
  160. // Initialize BDL info.
  161. //
  162. stBDList.pBDEntry = NULL;
  163. stBDList.pMapData = NULL;
  164. stBDList.nHead = 0;
  165. stBDList.nTail = 0;
  166. stBDList.ulTagCounter = 0;
  167. stBDList.nBDEntries = 0;
  168. //
  169. // Save miniport pointer and addref it.
  170. //
  171. Wave = Miniport_;
  172. Wave->AddRef ();
  173. //
  174. // Save portstream interface pointer and addref it.
  175. //
  176. PortStream = PortStream_;
  177. PortStream->AddRef ();
  178. //
  179. // Save channel ID and capture flag.
  180. //
  181. Channel = Channel_;
  182. Capture = Capture_;
  183. //
  184. // Save data format and current sample rate.
  185. //
  186. DataFormat = (PKSDATAFORMAT_WAVEFORMATEX)DataFormat_;
  187. CurrentRate = DataFormat->WaveFormatEx.nSamplesPerSec;
  188. NumberOfChannels = DataFormat->WaveFormatEx.nChannels;
  189. //
  190. // Initialize the BDL spinlock.
  191. //
  192. KeInitializeSpinLock (&MapLock);
  193. //
  194. // Create a service group (a DPC abstraction/helper) to help with
  195. // interrupts.
  196. //
  197. ntStatus = PcNewServiceGroup (&ServiceGroup, NULL);
  198. if (!NT_SUCCESS (ntStatus))
  199. {
  200. DOUT (DBG_ERROR, ("Failed to create a service group!"));
  201. return ntStatus;
  202. }
  203. //
  204. // Pass the ServiceGroup pointer to portcls.
  205. //
  206. *ServiceGroup_ = ServiceGroup;
  207. ServiceGroup->AddRef ();
  208. //
  209. // Setup the Buffer Descriptor List (BDL)
  210. // Allocate 32 entries of 8 bytes (one BDL entry). We allocate two tables
  211. // because we need one table as a backup.
  212. // The pointer is aligned on a 8 byte boundary (that's what we need).
  213. //
  214. stBDList.pBDEntry = (tBDEntry *)HalAllocateCommonBuffer (Wave->AdapterObject,
  215. MAX_BDL_ENTRIES * sizeof (tBDEntry) * 2,
  216. &stBDList.PhysAddr,
  217. FALSE);
  218. if (!stBDList.pBDEntry)
  219. {
  220. DOUT (DBG_ERROR, ("Failed HalAllocateCommonBuffer!"));
  221. return STATUS_INSUFFICIENT_RESOURCES;
  222. }
  223. // calculate the (backup) pointer.
  224. stBDList.pBDEntryBackup = (tBDEntry *)stBDList.pBDEntry + MAX_BDL_ENTRIES;
  225. //
  226. // Allocate a buffer for the 32 possible mappings. We allocate two tables
  227. // because we need one table as a backup
  228. //
  229. stBDList.pMapData =
  230. (tMapData *)ExAllocatePool (NonPagedPool, sizeof(tMapData) *
  231. MAX_BDL_ENTRIES * 2);
  232. if (!stBDList.pMapData)
  233. {
  234. DOUT (DBG_ERROR, ("Failed to allocate the back up buffer!"));
  235. return STATUS_INSUFFICIENT_RESOURCES;
  236. }
  237. // calculate the (backup) pointer.
  238. stBDList.pMapDataBackup = stBDList.pMapData + MAX_BDL_ENTRIES;
  239. //
  240. // Store the base address of this DMA engine.
  241. //
  242. if (Capture)
  243. {
  244. //
  245. // could be PCM or MIC capture
  246. //
  247. if (Channel == PIN_WAVEIN_OFFSET)
  248. {
  249. // Base address for DMA registers.
  250. m_ulBDAddr = PI_BDBAR;
  251. }
  252. else
  253. {
  254. // Base address for DMA registers.
  255. m_ulBDAddr = MC_BDBAR;
  256. }
  257. }
  258. else // render
  259. {
  260. // Base address for DMA registers.
  261. m_ulBDAddr = PO_BDBAR;
  262. }
  263. //
  264. // Reset the DMA and set the BD list pointer.
  265. //
  266. ResetDMA ();
  267. //
  268. // Reset the position pointers.
  269. //
  270. TotalBytesMapped = 0;
  271. TotalBytesReleased = 0;
  272. //
  273. // Now set the requested sample rate. In case of a failure, the object
  274. // gets destroyed and releases all memory etc.
  275. //
  276. ntStatus = SetFormat (DataFormat_);
  277. if (!NT_SUCCESS (ntStatus))
  278. {
  279. DOUT (DBG_ERROR, ("Stream init SetFormat call failed!"));
  280. return ntStatus;
  281. }
  282. //
  283. // Initialize the device state.
  284. //
  285. m_PowerState = PowerDeviceD0;
  286. PPREFETCHOFFSET PreFetchOffset;
  287. //
  288. // Query for the new interface "PreFetchOffset" and use
  289. // function offered there in case the interface is offered.
  290. //
  291. if (NT_SUCCESS(PortStream->QueryInterface(IID_IPreFetchOffset, (PVOID *)&PreFetchOffset)))
  292. {
  293. // why don't we pad by 32 sample frames
  294. PreFetchOffset->SetPreFetchOffset(32 * (DataFormat->WaveFormatEx.nChannels * 2));
  295. PreFetchOffset->Release();
  296. }
  297. //
  298. // Store the stream pointer, it is used by the ISR.
  299. //
  300. Wave->Streams[Channel] = this;
  301. return STATUS_SUCCESS;
  302. }
  303. /*****************************************************************************
  304. * CMiniportWaveICHStream::NonDelegatingQueryInterface
  305. *****************************************************************************
  306. * Obtains an interface. This function works just like a COM QueryInterface
  307. * call and is used if the object is not being aggregated.
  308. */
  309. STDMETHODIMP_(NTSTATUS) CMiniportWaveICHStream::NonDelegatingQueryInterface
  310. (
  311. IN REFIID Interface,
  312. OUT PVOID * Object
  313. )
  314. {
  315. PAGED_CODE ();
  316. ASSERT (Object);
  317. DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::NonDelegatingQueryInterface]"));
  318. //
  319. // Convert for IID_IMiniportWavePciStream
  320. //
  321. if (IsEqualGUIDAligned (Interface, IID_IMiniportWavePciStream))
  322. {
  323. *Object = (PVOID)(PMINIPORTWAVEPCISTREAM)this;
  324. }
  325. //
  326. // Convert for IID_IServiceSink
  327. //
  328. else if (IsEqualGUIDAligned (Interface, IID_IServiceSink))
  329. {
  330. *Object = (PVOID)(PSERVICESINK)this;
  331. }
  332. //
  333. // Convert for IID_IDrmAudioStream
  334. //
  335. else if (IsEqualGUIDAligned (Interface, IID_IDrmAudioStream))
  336. {
  337. *Object = (PVOID)(PDRMAUDIOSTREAM)this;
  338. }
  339. //
  340. // Convert for IID_IUnknown
  341. //
  342. else if (IsEqualGUIDAligned (Interface, IID_IUnknown))
  343. {
  344. *Object = (PVOID)(PUNKNOWN)(PMINIPORTWAVEPCISTREAM)this;
  345. }
  346. else
  347. {
  348. *Object = NULL;
  349. return STATUS_INVALID_PARAMETER;
  350. }
  351. ((PUNKNOWN)*Object)->AddRef ();
  352. return STATUS_SUCCESS;
  353. }
  354. /*****************************************************************************
  355. * CMiniportWaveICHStream::GetAllocatorFraming
  356. *****************************************************************************
  357. * Returns the framing requirements for this device.
  358. * That is sample size (for one sample) and preferred frame (buffer) size.
  359. */
  360. STDMETHODIMP_(NTSTATUS) CMiniportWaveICHStream::GetAllocatorFraming
  361. (
  362. OUT PKSALLOCATOR_FRAMING AllocatorFraming
  363. )
  364. {
  365. PAGED_CODE ();
  366. ULONG SampleSize;
  367. DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::GetAllocatorFraming]"));
  368. //
  369. // Determine sample size in bytes. Always number of
  370. // channels * 2 (because 16-bit).
  371. //
  372. SampleSize = DataFormat->WaveFormatEx.nChannels * 2;
  373. //
  374. // Report the suggested requirements.
  375. //
  376. AllocatorFraming->RequirementsFlags =
  377. KSALLOCATOR_REQUIREMENTF_SYSTEM_MEMORY |
  378. KSALLOCATOR_REQUIREMENTF_PREFERENCES_ONLY;
  379. AllocatorFraming->Frames = 8;
  380. //
  381. // Currently, arbitrarily selecting 10ms as the frame target size.
  382. //
  383. // This value needs to be sample block aligned for ICH to work correctly.
  384. // Assumes 100Hz minimum sample rate (otherwise FrameSize is 0 bytes)
  385. //
  386. AllocatorFraming->FrameSize = SampleSize * (DataFormat->WaveFormatEx.nSamplesPerSec / 100);
  387. AllocatorFraming->FileAlignment = FILE_LONG_ALIGNMENT;
  388. AllocatorFraming->PoolType = NonPagedPool;
  389. return STATUS_SUCCESS;
  390. }
  391. /*****************************************************************************
  392. * CMiniportWaveICHStream::SetFormat
  393. *****************************************************************************
  394. * This routine tests for proper data format (calls wave miniport) and sets
  395. * or changes the stream data format.
  396. * To figure out if the codec supports the sample rate, we just program the
  397. * sample rate and read it back. If it matches we return happy, if not then
  398. * we restore the sample rate and return unhappy.
  399. * We fail this routine if we are currently running (playing or recording).
  400. */
  401. STDMETHODIMP_(NTSTATUS) CMiniportWaveICHStream::SetFormat
  402. (
  403. IN PKSDATAFORMAT Format
  404. )
  405. {
  406. PAGED_CODE ();
  407. ASSERT (Format);
  408. ULONG TempRate;
  409. DWORD dwControlReg;
  410. DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::SetFormat]"));
  411. //
  412. // Change sample rate when we are in the stop or pause states - not
  413. // while running!
  414. //
  415. if (DMAEngineState & DMA_ENGINE_ON)
  416. {
  417. return STATUS_UNSUCCESSFUL;
  418. }
  419. //
  420. // Ensure format falls in proper range and is supported.
  421. //
  422. NTSTATUS ntStatus = Wave->TestDataFormat (Format, (WavePins)(Channel << 1));
  423. if (!NT_SUCCESS (ntStatus))
  424. return ntStatus;
  425. //
  426. // Retrieve wave format portion.
  427. //
  428. PWAVEFORMATPCMEX waveFormat = (PWAVEFORMATPCMEX)(Format + 1);
  429. //
  430. // Save current rate in this context.
  431. //
  432. TempRate = waveFormat->Format.nSamplesPerSec;
  433. //
  434. // Check if we have a codec with one sample rate converter and there are streams
  435. // already open.
  436. //
  437. if (Wave->Streams[PIN_WAVEIN_OFFSET] && Wave->Streams[PIN_WAVEOUT_OFFSET] &&
  438. !Wave->AdapterCommon->GetNodeConfig (NODEC_PCM_VSR_INDEPENDENT_RATES))
  439. {
  440. //
  441. // Figure out at which sample rate the other stream is running.
  442. //
  443. ULONG ulFrequency;
  444. if (Wave->Streams[PIN_WAVEIN_OFFSET] == this)
  445. ulFrequency = Wave->Streams[PIN_WAVEOUT_OFFSET]->CurrentRate;
  446. else
  447. ulFrequency = Wave->Streams[PIN_WAVEIN_OFFSET]->CurrentRate;
  448. //
  449. // Check if this sample rate is requested sample rate.
  450. //
  451. if (ulFrequency != TempRate)
  452. {
  453. return STATUS_UNSUCCESSFUL;
  454. }
  455. }
  456. //
  457. // Program the ICH to support n channels.
  458. //
  459. if (Channel == PIN_WAVEOUT_OFFSET)
  460. {
  461. dwControlReg = Wave->AdapterCommon->ReadBMControlRegister32 (GLOB_CNT);
  462. dwControlReg = (dwControlReg & 0x03F) |
  463. (((waveFormat->Format.nChannels >> 1) - 1) * GLOB_CNT_PCM4);
  464. Wave->AdapterCommon->WriteBMControlRegister (GLOB_CNT, dwControlReg);
  465. }
  466. //
  467. // Check for rate support by hardware. If it is supported, then update
  468. // hardware registers else return not implemented and audio stack will
  469. // handle it.
  470. //
  471. if (Capture)
  472. {
  473. if (Channel == PIN_WAVEIN_OFFSET)
  474. {
  475. ntStatus = Wave->AdapterCommon->
  476. ProgramSampleRate (AC97REG_RECORD_SAMPLERATE, TempRate);
  477. }
  478. else
  479. {
  480. ntStatus = Wave->AdapterCommon->
  481. ProgramSampleRate (AC97REG_MIC_SAMPLERATE, TempRate);
  482. }
  483. }
  484. else
  485. {
  486. //
  487. // In the playback case we might need to update several DACs
  488. // with the new sample rate.
  489. //
  490. ntStatus = Wave->AdapterCommon->
  491. ProgramSampleRate (AC97REG_FRONT_SAMPLERATE, TempRate);
  492. if (Wave->AdapterCommon->GetNodeConfig (NODEC_SURROUND_DAC_PRESENT))
  493. {
  494. ntStatus = Wave->AdapterCommon->
  495. ProgramSampleRate (AC97REG_SURROUND_SAMPLERATE, TempRate);
  496. }
  497. if (Wave->AdapterCommon->GetNodeConfig (NODEC_LFE_DAC_PRESENT))
  498. {
  499. ntStatus = Wave->AdapterCommon->
  500. ProgramSampleRate (AC97REG_LFE_SAMPLERATE, TempRate);
  501. }
  502. }
  503. if (NT_SUCCESS (ntStatus))
  504. {
  505. //
  506. // print information and save the format information.
  507. //
  508. DataFormat = (PKSDATAFORMAT_WAVEFORMATEX)Format;
  509. CurrentRate = TempRate;
  510. NumberOfChannels = waveFormat->Format.nChannels;
  511. }
  512. return ntStatus;
  513. }
  514. /*****************************************************************************
  515. * CMiniportWaveICHStream::SetContentId
  516. *****************************************************************************
  517. * This routine gets called by drmk.sys to pass the content to the driver.
  518. * The driver has to enforce the rights passed.
  519. */
  520. STDMETHODIMP_(NTSTATUS) CMiniportWaveICHStream::SetContentId
  521. (
  522. IN ULONG contentId,
  523. IN PCDRMRIGHTS drmRights
  524. )
  525. {
  526. PAGED_CODE ();
  527. DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::SetContentId]"));
  528. //
  529. // If "drmRights->DigitalOutputDisable" is set, we need to disable S/P-DIF.
  530. // Currently, we don't have knowledge about the S/P-DIF interface. However,
  531. // in case you expanded the driver with S/P-DIF features you need to disable
  532. // S/P-DIF or fail SetContentId. If you have HW that has S/P-DIF turned on
  533. // by default and you don't know how to turn off (or you cannot do that)
  534. // then you must fail SetContentId.
  535. //
  536. // In our case, we assume the codec has no S/P-DIF or disabled S/P-DIF by
  537. // default, so we can ignore the flag.
  538. //
  539. // Store the copyright flag. We have to disable PCM recording if it's set.
  540. //
  541. if (!Wave->AdapterCommon->GetMiniportTopology ())
  542. {
  543. DOUT (DBG_ERROR, ("Topology pointer not set!"));
  544. return STATUS_UNSUCCESSFUL;
  545. }
  546. else
  547. {
  548. Wave->AdapterCommon->GetMiniportTopology ()->
  549. SetCopyProtectFlag (drmRights->CopyProtect);
  550. }
  551. //
  552. // We assume that if we can enforce the rights, that the old content
  553. // will be destroyed. We don't need to store the content id since we
  554. // have only one playback channel, so we are finished here.
  555. //
  556. return STATUS_SUCCESS;
  557. }
  558. /*****************************************************************************
  559. * Non paged code begins here
  560. *****************************************************************************
  561. */
  562. #pragma code_seg()
  563. /*****************************************************************************
  564. * CMiniportWaveICHStream::PowerChangeNotify
  565. *****************************************************************************
  566. * This functions saves and maintains the stream state through power changes.
  567. */
  568. NTSTATUS CMiniportWaveICHStream::PowerChangeNotify
  569. (
  570. IN POWER_STATE NewState
  571. )
  572. {
  573. PAGED_CODE ();
  574. KIRQL OldIrql;
  575. NTSTATUS ntStatus = STATUS_SUCCESS;
  576. DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::PowerChangeNotify]"));
  577. //
  578. // We don't have to check the power state, that's already done by the wave
  579. // miniport.
  580. //
  581. DOUT (DBG_POWER, ("Changing state to D%d.",
  582. (ULONG)NewState.DeviceState - (ULONG)PowerDeviceD0));
  583. switch (NewState.DeviceState)
  584. {
  585. case PowerDeviceD0:
  586. //
  587. // If we are coming from D2 or D3 we have to restore the registers cause
  588. // there might have been a power loss.
  589. //
  590. if ((m_PowerState == PowerDeviceD3) || (m_PowerState == PowerDeviceD2))
  591. {
  592. //
  593. // The scatter gather list is already arranged. A reset of the DMA
  594. // brings all pointers to the default state. From there we can start.
  595. //
  596. // Acquire the mapping spin lock
  597. KeAcquireSpinLock (&MapLock,&OldIrql);
  598. ntStatus = ResetDMA ();
  599. // Restore the remaining DMA registers, that is last valid index
  600. // only if the index is not pointing to 0. Note that the index is
  601. // equal to head + entries.
  602. if (stBDList.nTail)
  603. {
  604. Wave->AdapterCommon->WriteBMControlRegister (m_ulBDAddr + X_LVI,
  605. (UCHAR)((stBDList.nTail - 1) & BDL_MASK));
  606. }
  607. // Release the mapping spin lock
  608. KeReleaseSpinLock (&MapLock,OldIrql);
  609. }
  610. break;
  611. case PowerDeviceD1:
  612. // Here we do nothing. The device has still enough power to keep all
  613. // it's register values.
  614. break;
  615. case PowerDeviceD2:
  616. case PowerDeviceD3:
  617. //
  618. // If we power down to D2 or D3 we might loose power, so we have to be
  619. // aware of the DMA engine resetting. In that case a play would start
  620. // with scatter gather entry 0 (the current index is read only).
  621. // We just rearrange the scatter gather list (like we do on
  622. // RevokeMappings) so that the current buffer which is played is at
  623. // entry 0.
  624. //
  625. // Acquire the mapping spin lock
  626. KeAcquireSpinLock (&MapLock,&OldIrql);
  627. // Disable interrupts and stop DMA just in case.
  628. Wave->AdapterCommon->WriteBMControlRegister (m_ulBDAddr + X_CR, (UCHAR)0);
  629. // Get current index
  630. int nCurrentIndex = (int)Wave->AdapterCommon->
  631. ReadBMControlRegister8 (m_ulBDAddr + X_CIV);
  632. //
  633. // First move the BD list to the beginning.
  634. //
  635. // In case the DMA engine was stopped, current index may point to an
  636. // empty BD entry. When we start the DMA engine it would then play this
  637. // (undefined) entry, so we check here for that condition.
  638. //
  639. if ((nCurrentIndex == ((stBDList.nHead - 1) & BDL_MASK)) &&
  640. (stBDList.nBDEntries != MAX_BDL_ENTRIES - 1))
  641. {
  642. nCurrentIndex = stBDList.nHead; // point to head
  643. }
  644. //
  645. // Move BD list to (0-((current - head) & mask)) & mask, where
  646. // ((current - head) & mask) is the difference between head and
  647. // current index, no matter where they are :)
  648. //
  649. MoveBDList (stBDList.nHead, (stBDList.nTail - 1) & BDL_MASK,
  650. (0 - ((nCurrentIndex - stBDList.nHead) & BDL_MASK)) & BDL_MASK);
  651. //
  652. // Update structure.
  653. //
  654. stBDList.nHead = (0 - ((nCurrentIndex - stBDList.nHead) & BDL_MASK)) &
  655. BDL_MASK;
  656. stBDList.nTail = (stBDList.nHead + stBDList.nBDEntries) & BDL_MASK;
  657. // release the mapping spin lock
  658. KeReleaseSpinLock (&MapLock,OldIrql);
  659. break;
  660. }
  661. //
  662. // Save the new state. This local value is used to determine when to
  663. // cache property accesses and when to permit the driver from accessing
  664. // the hardware.
  665. //
  666. m_PowerState = NewState.DeviceState;
  667. DOUT (DBG_POWER, ("Entering D%d",
  668. (ULONG)m_PowerState - (ULONG)PowerDeviceD0));
  669. return ntStatus;
  670. }
  671. /*****************************************************************************
  672. * CMiniportWaveICHStream::SetState
  673. *****************************************************************************
  674. * This routine sets/changes the DMA engine state to play, stop, or pause
  675. */
  676. STDMETHODIMP_(NTSTATUS) CMiniportWaveICHStream::SetState
  677. (
  678. IN KSSTATE State
  679. )
  680. {
  681. PAGED_CODE (); // Gets called at PASSIVE_LEVEL
  682. KIRQL OldIrql;
  683. DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::SetState]"));
  684. DOUT (DBG_STREAM, ("SetState to %d", State));
  685. //
  686. // Start or stop the DMA engine dependent of the state.
  687. //
  688. switch (State)
  689. {
  690. case KSSTATE_STOP:
  691. // acquire the mapping spin lock
  692. KeAcquireSpinLock (&MapLock,&OldIrql);
  693. // Just pause DMA. If we have mappings left in our queue,
  694. // portcls will call RevokeMappings to destroy them.
  695. PauseDMA ();
  696. // release the mapping spin lock
  697. KeReleaseSpinLock (&MapLock,OldIrql);
  698. // Release processed mappings
  699. ReleaseUsedMappings ();
  700. // Reset the position counters.
  701. TotalBytesMapped = TotalBytesReleased = 0;
  702. break;
  703. case KSSTATE_ACQUIRE:
  704. case KSSTATE_PAUSE:
  705. // acquire the mapping spin lock
  706. KeAcquireSpinLock (&MapLock,&OldIrql);
  707. // pause now.
  708. PauseDMA ();
  709. // release the mapping spin lock
  710. KeReleaseSpinLock (&MapLock,OldIrql);
  711. // Release processed mappings
  712. ReleaseUsedMappings ();
  713. break;
  714. case KSSTATE_RUN:
  715. //
  716. // Let's rock.
  717. //
  718. // Make sure we are not running already.
  719. if (DMAEngineState & DMA_ENGINE_ON)
  720. {
  721. return STATUS_SUCCESS;
  722. }
  723. // Release processed mappings.
  724. ReleaseUsedMappings ();
  725. // Get new mappings.
  726. GetNewMappings ();
  727. // acquire the mapping spin lock
  728. KeAcquireSpinLock (&MapLock,&OldIrql);
  729. // Kick DMA again just in case.
  730. ResumeDMA ();
  731. // release the mapping spin lock
  732. KeReleaseSpinLock (&MapLock,OldIrql);
  733. break;
  734. }
  735. return STATUS_SUCCESS;
  736. }
  737. /*****************************************************************************
  738. * CMiniportWaveICHStream::MoveBDList
  739. *****************************************************************************
  740. * Moves the BDList.
  741. * This function is used to remove entries from the scatter gather list or to
  742. * move the valid entries to the top. The mapping table which is hard linked
  743. * to the scatter gather entries is moved too.
  744. * The function does not change any variables in tBDList.
  745. * The mapping spin lock must be held when calling this routine.
  746. * We use this function to remove mappings (RevokeMappings) or to rearrange the
  747. * list for powerdown/up management (the DMA starts at position zero again).
  748. * Note that there is a simple way of doing this also. When you zero the buffer
  749. * length in the scatter gather, the DMA engine ignores the entry and continues
  750. * with the next. But our way is more generic and if you ever want to port the
  751. * driver to another DMA engine you might be thankful for this code.
  752. */
  753. void CMiniportWaveICHStream::MoveBDList
  754. (
  755. IN int nFirst,
  756. IN int nLast,
  757. IN int nNewPos
  758. )
  759. {
  760. DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::MoveBDList]"));
  761. //
  762. // Print information about the scatter gather list.
  763. //
  764. DOUT (DBG_DMA, ("Moving BD entry %d-%d to %d.", nFirst, nLast, nNewPos));
  765. //
  766. // First copy the tables to a save place.
  767. //
  768. RtlCopyMemory ((PVOID)stBDList.pBDEntryBackup,
  769. (PVOID)stBDList.pBDEntry,
  770. sizeof (tBDEntry) * MAX_BDL_ENTRIES);
  771. RtlCopyMemory ((PVOID)stBDList.pMapDataBackup,
  772. (PVOID)stBDList.pMapData,
  773. sizeof (tMapData) * MAX_BDL_ENTRIES);
  774. //
  775. // We move all the entries in blocks to the new position.
  776. //
  777. int nBlockCounter = 0;
  778. do
  779. {
  780. nBlockCounter++;
  781. //
  782. // We must copy the block when the index wraps around (ring buffer)
  783. // or we are at the last entry.
  784. //
  785. if (((nNewPos + nBlockCounter) == MAX_BDL_ENTRIES) || // wrap around
  786. ((nFirst + nBlockCounter) == MAX_BDL_ENTRIES) || // wrap around
  787. ((nFirst + nBlockCounter) == (nLast + 1))) // last entry
  788. {
  789. //
  790. // copy one block (multiple entries).
  791. //
  792. RtlCopyMemory ((PVOID)&stBDList.pBDEntry[nNewPos],
  793. (PVOID)&stBDList.pBDEntryBackup[nFirst],
  794. sizeof (tBDEntry) * nBlockCounter);
  795. RtlCopyMemory ((PVOID)&stBDList.pMapData[nNewPos],
  796. (PVOID)&stBDList.pMapDataBackup[nFirst],
  797. sizeof (tMapData) * nBlockCounter);
  798. // adjust the index
  799. nNewPos = (nNewPos + nBlockCounter) & BDL_MASK;
  800. nFirst = (nFirst + nBlockCounter) & BDL_MASK;
  801. nBlockCounter = 0;
  802. }
  803. // nBlockCounter should be zero when the end condition hits.
  804. } while (((nFirst + nBlockCounter - 1) & BDL_MASK) != nLast);
  805. }
  806. /*****************************************************************************
  807. * CMiniportWaveICHStream::Service
  808. *****************************************************************************
  809. * This routine is called by the port driver in response to the interrupt
  810. * service routine requesting service on the stream's service group.
  811. * Requesting service on the service group results in a DPC being scheduled
  812. * that calls this routine when it runs.
  813. */
  814. STDMETHODIMP_(void) CMiniportWaveICHStream::Service (void)
  815. {
  816. DOUT (DBG_PRINT, ("Service"));
  817. // release all mappings
  818. ReleaseUsedMappings ();
  819. // get new mappings
  820. GetNewMappings ();
  821. }
  822. /*****************************************************************************
  823. * CMiniportWaveICHStream::NormalizePhysicalPosition
  824. *****************************************************************************
  825. * Given a physical position based on the actual number of bytes transferred,
  826. * this function converts the position to a time-based value of 100ns units.
  827. */
  828. STDMETHODIMP_(NTSTATUS) CMiniportWaveICHStream::NormalizePhysicalPosition
  829. (
  830. IN OUT PLONGLONG PhysicalPosition
  831. )
  832. {
  833. ULONG SampleSize;
  834. DOUT (DBG_PRINT, ("NormalizePhysicalPosition"));
  835. //
  836. // Determine the sample size in bytes
  837. //
  838. SampleSize = DataFormat->WaveFormatEx.nChannels * 2;
  839. //
  840. // Calculate the time in 100ns steps.
  841. //
  842. *PhysicalPosition = (_100NS_UNITS_PER_SECOND / SampleSize *
  843. *PhysicalPosition) / CurrentRate;
  844. return STATUS_SUCCESS;
  845. }
  846. /*****************************************************************************
  847. * CMiniportWaveICHStream::GetPosition
  848. *****************************************************************************
  849. * Gets the stream position. This is a byte count of the current position of
  850. * a stream running on a particular DMA engine. We must return a sample
  851. * accurate count or the WaveDrv32 wave drift tests (35.2 & 36.2) will fail.
  852. *
  853. * The position is the sum of three parts:
  854. * 1) The total number of bytes in released buffers
  855. * 2) The position in the current buffer.
  856. * 3) The total number of bytes in played but not yet released buffers
  857. */
  858. STDMETHODIMP_(NTSTATUS) CMiniportWaveICHStream::GetPosition
  859. (
  860. OUT PULONGLONG Position
  861. )
  862. {
  863. KIRQL OldIrql;
  864. UCHAR nCurrentIndex = 0;
  865. DWORD RegisterX_PICB;
  866. ASSERT (Position);
  867. //
  868. // Acquire the mapping spin lock.
  869. //
  870. KeAcquireSpinLock (&MapLock, &OldIrql);
  871. //
  872. // Start with TotalBytesReleased (mappings released).
  873. //
  874. *Position = TotalBytesReleased;
  875. //
  876. // If we have entries in the list, we may have buffers that have not been
  877. // released but have been at least partially played.
  878. //
  879. if (stBDList.nBDEntries)
  880. {
  881. //
  882. // Repeat this until we get the same reading twice. This will prevent
  883. // jumps when we are near the end of the buffer.
  884. //
  885. do
  886. {
  887. nCurrentIndex = Wave->AdapterCommon->
  888. ReadBMControlRegister8 (m_ulBDAddr + X_CIV);
  889. RegisterX_PICB = (DWORD)Wave->AdapterCommon->ReadBMControlRegister16 (m_ulBDAddr + X_PICB);
  890. } while (nCurrentIndex != (int)Wave->AdapterCommon->
  891. ReadBMControlRegister8 (m_ulBDAddr + X_CIV));
  892. //
  893. // If we never released a buffer and register X_PICB is zero then the DMA was not
  894. // initialized yet and the position should be zero.
  895. //
  896. if (RegisterX_PICB || nCurrentIndex || TotalBytesReleased)
  897. {
  898. //
  899. // Add in our position in the current buffer. The read returns the
  900. // amount left in the buffer.
  901. //
  902. *Position += (stBDList.pMapData[nCurrentIndex].ulBufferLength - (RegisterX_PICB << 1));
  903. //
  904. // Total any buffers that have been played and not released.
  905. //
  906. if (nCurrentIndex != ((stBDList.nHead -1) & BDL_MASK))
  907. {
  908. int i = stBDList.nHead;
  909. while (i != nCurrentIndex)
  910. {
  911. *Position += (ULONGLONG)stBDList.pMapData[i].ulBufferLength;
  912. i = (i + 1) & BDL_MASK;
  913. }
  914. }
  915. }
  916. }
  917. DOUT (DBG_POSITION, ("[GetPosition] POS: %08x'%08x\n", (DWORD)(*Position >> 32), (DWORD)*Position));
  918. //
  919. // Release the mapping spin lock.
  920. //
  921. KeReleaseSpinLock (&MapLock, OldIrql);
  922. return STATUS_SUCCESS;
  923. }
  924. /*****************************************************************************
  925. * CMiniportWaveICHStream::RevokeMappings
  926. *****************************************************************************
  927. * This routine is used by the port to revoke mappings previously delivered
  928. * to the miniport stream that have not yet been unmapped. This would
  929. * typically be called in response to an I/O cancellation request.
  930. */
  931. STDMETHODIMP_(NTSTATUS) CMiniportWaveICHStream::RevokeMappings
  932. (
  933. IN PVOID FirstTag,
  934. IN PVOID LastTag,
  935. OUT PULONG MappingsRevoked
  936. )
  937. {
  938. ASSERT (MappingsRevoked);
  939. KIRQL OldIrql;
  940. ULONG ulOldDMAEngineState;
  941. int nCurrentIndex, nSearchIndex, nFirst, nLast, nNumMappings;
  942. DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::RevokeMappings]"));
  943. //
  944. // print information about the scatter gather list.
  945. //
  946. DOUT (DBG_DMA, ("Head %d, Tail %d, Tag counter %d, Entries %d.",
  947. stBDList.nHead, stBDList.nTail, stBDList.ulTagCounter,
  948. stBDList.nBDEntries));
  949. //
  950. // Start accessing the mappings.
  951. //
  952. KeAcquireSpinLock (&MapLock, &OldIrql);
  953. //
  954. // Save old DMA engine state.
  955. //
  956. ulOldDMAEngineState = DMAEngineState;
  957. //
  958. // First stop the DMA engine so it won't process the next buffer in the
  959. // scatter gather list which might be one of the revoked buffers.
  960. //
  961. PauseDMA ();
  962. // Get current index
  963. nCurrentIndex = Wave->AdapterCommon->
  964. ReadBMControlRegister8 (m_ulBDAddr + X_CIV);
  965. //
  966. // We always rearrange the scatter gather list. That means we reset the DMA
  967. // engine and move the BD list so that the entry where the current index
  968. // pointed to is located at position 0.
  969. //
  970. ResetDMA ();
  971. //
  972. // Return immediately if we just have 1 entry in our BD list.
  973. //
  974. if (!stBDList.nBDEntries || (stBDList.nBDEntries == 1))
  975. {
  976. *MappingsRevoked = stBDList.nBDEntries;
  977. stBDList.nHead = stBDList.nTail = stBDList.nBDEntries = 0;
  978. //
  979. // CIV and LVI of DMA registers are set to 0 already.
  980. //
  981. KeReleaseSpinLock (&MapLock, OldIrql);
  982. return STATUS_SUCCESS;
  983. }
  984. //
  985. // First move the BD list to the beginning. In case the DMA engine was
  986. // stopped, current index may point to an empty BD entry.
  987. //
  988. if ((nCurrentIndex == ((stBDList.nHead - 1) & BDL_MASK)) &&
  989. (stBDList.nBDEntries != MAX_BDL_ENTRIES - 1))
  990. {
  991. nCurrentIndex = stBDList.nHead; // point to head
  992. }
  993. //
  994. // Move BD list to (0-((current - head) & mask)) & mask
  995. // where ((current - head) & mask) is the difference between head and
  996. // current index, no matter where they are :)
  997. //
  998. MoveBDList (stBDList.nHead, (stBDList.nTail - 1) & BDL_MASK,
  999. (0 - ((nCurrentIndex - stBDList.nHead) & BDL_MASK)) & BDL_MASK);
  1000. //
  1001. // Update structure.
  1002. //
  1003. stBDList.nHead = (0 - ((nCurrentIndex - stBDList.nHead) & BDL_MASK)) &
  1004. BDL_MASK;
  1005. stBDList.nTail = (stBDList.nHead + stBDList.nBDEntries) & BDL_MASK;
  1006. //
  1007. // Then we have to search for the tags. If we wouldn't have to rearrange the
  1008. // scatter gather list all the time, then we could use the tag as an index
  1009. // to the array, but the only way to clear DMA caches is a reset which has
  1010. // the side-effect that we have to rearrange the BD list (see above).
  1011. //
  1012. // search for first...
  1013. //
  1014. nSearchIndex = stBDList.nHead;
  1015. do
  1016. {
  1017. if ((void *)ULongToPtr(stBDList.pMapData[nSearchIndex].ulTag) == FirstTag)
  1018. break;
  1019. nSearchIndex = (nSearchIndex + 1) & BDL_MASK;
  1020. } while (nSearchIndex != stBDList.nTail);
  1021. nFirst = nSearchIndex;
  1022. //
  1023. // Search for last...
  1024. //
  1025. nSearchIndex = stBDList.nHead;
  1026. do
  1027. {
  1028. if ((void *)ULongToPtr(stBDList.pMapData[nSearchIndex].ulTag) == LastTag)
  1029. break;
  1030. nSearchIndex = (nSearchIndex + 1) & BDL_MASK;
  1031. } while (nSearchIndex != stBDList.nTail);
  1032. nLast = nSearchIndex;
  1033. //
  1034. // Check search result.
  1035. //
  1036. if ((nFirst == stBDList.nTail) || (nLast == stBDList.nTail))
  1037. {
  1038. DOUT (DBG_ERROR, ("!!! Entry not found !!!"));
  1039. //
  1040. // restart DMA in case it was running
  1041. //
  1042. if ((ulOldDMAEngineState & DMA_ENGINE_ON) && stBDList.nBDEntries)
  1043. ResumeDMA ();
  1044. *MappingsRevoked = 0;
  1045. KeReleaseSpinLock (&MapLock, OldIrql);
  1046. return STATUS_UNSUCCESSFUL; // one of the tags not found
  1047. }
  1048. // Print the index numbers found.
  1049. DOUT (DBG_DMA, ("Removing entries %d (%d) to %d (%d).", nFirst, FirstTag,
  1050. nLast, LastTag));
  1051. //
  1052. // Calculate the entries between the indizes.
  1053. //
  1054. if (nLast < nFirst)
  1055. {
  1056. nNumMappings = ((nLast + MAX_BDL_ENTRIES) - nFirst) + 1;
  1057. }
  1058. else
  1059. {
  1060. nNumMappings = (nLast - nFirst) + 1;
  1061. }
  1062. //
  1063. // Print debug inormation.
  1064. //
  1065. DOUT (DBG_DMA, ("Found entries: %d-%d, %d entries.", nFirst, nLast,
  1066. nNumMappings));
  1067. //
  1068. // Now remove the revoked buffers. Move the BD list and modify the
  1069. // status information.
  1070. //
  1071. if (nFirst < stBDList.nTail)
  1072. {
  1073. //
  1074. // In this case, both first and last are >= the current index (0)
  1075. //
  1076. if (nLast != ((stBDList.nTail - 1) & BDL_MASK))
  1077. {
  1078. //
  1079. // Not the last entry, so move the BD list + mappings.
  1080. //
  1081. MoveBDList ((nLast + 1) & BDL_MASK, (stBDList.nTail - 1) & BDL_MASK,
  1082. nFirst);
  1083. }
  1084. stBDList.nTail = (stBDList.nTail - nNumMappings) & BDL_MASK;
  1085. }
  1086. //
  1087. // In this case, at least first is "<" than current index (0)
  1088. //
  1089. else
  1090. {
  1091. //
  1092. // Check for last.
  1093. //
  1094. if (nLast < stBDList.nTail)
  1095. {
  1096. //
  1097. // Last is ">=" current index and first is "<" current index (0).
  1098. // Remove MAX_DBL_ENTRIES - first entries in front of current index.
  1099. //
  1100. if (nFirst != stBDList.nHead)
  1101. {
  1102. //
  1103. // Move from head towards current index.
  1104. //
  1105. MoveBDList (stBDList.nHead, nFirst - 1,
  1106. (stBDList.nHead + (MAX_BDL_ENTRIES - nFirst)) &
  1107. BDL_MASK);
  1108. }
  1109. //
  1110. // Adjust head.
  1111. //
  1112. stBDList.nHead = (stBDList.nHead + (MAX_BDL_ENTRIES - nFirst)) &
  1113. BDL_MASK;
  1114. //
  1115. // Remove nLast entries from CIV to tail.
  1116. //
  1117. if (nLast != ((stBDList.nTail - 1) & BDL_MASK))
  1118. {
  1119. //
  1120. // Not the last entry, so move the BD list + mappings.
  1121. //
  1122. MoveBDList (nLast + 1, (stBDList.nTail - 1) & BDL_MASK, 0);
  1123. }
  1124. //
  1125. // Adjust tail.
  1126. //
  1127. stBDList.nTail = (stBDList.nTail - (nLast + 1)) & BDL_MASK;
  1128. }
  1129. //
  1130. // Last is "<" current index and first is "<" current index (0).
  1131. //
  1132. else
  1133. {
  1134. //
  1135. // Remove nNumMappings entries in front of current index.
  1136. //
  1137. if (nFirst != stBDList.nHead)
  1138. {
  1139. //
  1140. // Move from head towards current index.
  1141. //
  1142. MoveBDList (stBDList.nHead, nFirst - 1,
  1143. (nLast - nNumMappings) + 1);
  1144. }
  1145. //
  1146. // Adjust head.
  1147. //
  1148. stBDList.nHead = (stBDList.nHead + nNumMappings) & BDL_MASK;
  1149. }
  1150. }
  1151. //
  1152. // In all cases, reduce the number of mappings.
  1153. //
  1154. stBDList.nBDEntries -= nNumMappings;
  1155. //
  1156. // Print debug information.
  1157. //
  1158. DOUT (DBG_DMA, ("Number of mappings is now %d, Head is %d, Tail is %d",
  1159. stBDList.nBDEntries, stBDList.nHead, stBDList.nTail));
  1160. //
  1161. // Reprogram the last valid index only when tail != 0
  1162. //
  1163. if (stBDList.nTail)
  1164. {
  1165. Wave->AdapterCommon->WriteBMControlRegister (m_ulBDAddr + X_LVI,
  1166. (UCHAR)(stBDList.nTail - 1 & BDL_MASK));
  1167. }
  1168. //
  1169. // Just un-pause the DMA engine if it was running before and there are
  1170. // still entries left and tail != 0.
  1171. //
  1172. if ((ulOldDMAEngineState & DMA_ENGINE_ON) && stBDList.nBDEntries
  1173. && stBDList.nTail)
  1174. {
  1175. ResumeDMA ();
  1176. }
  1177. //
  1178. // Release the mapping spin lock and return the number of mappings we
  1179. // revoked.
  1180. //
  1181. KeReleaseSpinLock (&MapLock, OldIrql);
  1182. *MappingsRevoked = nNumMappings;
  1183. return STATUS_SUCCESS;
  1184. }
  1185. /*****************************************************************************
  1186. * CMiniportWaveICHStream::MappingAvailable
  1187. *****************************************************************************
  1188. * This routine is called by the port driver to notify the stream that there
  1189. * are new mappings available. Note that this is ONLY called after the stream
  1190. * has previously had a GetMapping() call fail due to lack of available
  1191. * mappings.
  1192. */
  1193. STDMETHODIMP_(void) CMiniportWaveICHStream::MappingAvailable (void)
  1194. {
  1195. DOUT (DBG_PRINT, ("MappingAvailable"));
  1196. //
  1197. // Release processed mappings.
  1198. //
  1199. ReleaseUsedMappings ();
  1200. //
  1201. // Process the new mappings.
  1202. //
  1203. GetNewMappings ();
  1204. }
  1205. /*****************************************************************************
  1206. * CMiniportWaveICHStream::GetNewMappings
  1207. *****************************************************************************
  1208. * This routine is called when new mappings are available from the port driver.
  1209. * The routine places mappings into the input mapping queue. ICH can handle up
  1210. * to 32 entries (descriptors). We program the DMA registers if we have at least
  1211. * one mapping in the queue. The mapping spin lock must be held when calling
  1212. * this routine.
  1213. */
  1214. NTSTATUS CMiniportWaveICHStream::GetNewMappings (void)
  1215. {
  1216. KIRQL OldIrql;
  1217. NTSTATUS ntStatus = STATUS_SUCCESS;
  1218. ULONG ulBytesMapped = 0;
  1219. int nInsertMappings = 0;
  1220. int nTail; // aut. variable
  1221. DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::GetNewMappings]"));
  1222. // acquire the mapping spin lock
  1223. KeAcquireSpinLock (&MapLock,&OldIrql);
  1224. #if (DBG)
  1225. if (Wave->AdapterCommon->ReadBMControlRegister16 (m_ulBDAddr + X_SR) & SR_CELV)
  1226. {
  1227. //
  1228. // We starve. :-(
  1229. //
  1230. DOUT (DBG_DMA, ("[GetNewMappings] We starved ... Head %d, Tail %d, Entries %d.",
  1231. stBDList.nHead, stBDList.nTail, stBDList.nBDEntries));
  1232. }
  1233. #endif
  1234. //
  1235. // Get available mappings up to the max of 31 that we can hold in the BDL.
  1236. //
  1237. while (stBDList.nBDEntries < (MAX_BDL_ENTRIES - 1))
  1238. {
  1239. //
  1240. // Get the information from the list.
  1241. //
  1242. ULONG Flags;
  1243. ULONG ulTag = stBDList.ulTagCounter++;
  1244. ULONG ulBufferLength;
  1245. PHYSICAL_ADDRESS PhysAddr;
  1246. PVOID VirtAddr;
  1247. // Release the mapping spin lock
  1248. KeReleaseSpinLock (&MapLock,OldIrql);
  1249. //
  1250. // Try to get the mapping from the port.
  1251. // Here comes the problem: When calling GetMapping or ReleaseMapping we
  1252. // cannot hold our spin lock. So we need to buffer the return values and
  1253. // stick the information into the structure later when we have the spin
  1254. // lock acquired.
  1255. //
  1256. ntStatus = PortStream->GetMapping ((PVOID)ULongToPtr(ulTag),
  1257. (PPHYSICAL_ADDRESS)&PhysAddr,
  1258. &VirtAddr,
  1259. &ulBufferLength,
  1260. &Flags);
  1261. // Acquire the mapping spin lock
  1262. KeAcquireSpinLock (&MapLock,&OldIrql);
  1263. //
  1264. // Quit this loop when we run out of mappings.
  1265. //
  1266. if (!NT_SUCCESS (ntStatus))
  1267. {
  1268. break;
  1269. }
  1270. // Sanity check: The audio stack will not give you data
  1271. // that you cannot handle, but an application could use
  1272. // DirectKS and send bad buffer down.
  1273. // One mapping needs to be <0x1FFFF bytes for mono
  1274. // streams on the ICH.
  1275. if (ulBufferLength > 0x1FFFE)
  1276. {
  1277. // That is a little too long. That should never happen.
  1278. DOUT (DBG_ERROR, ("[GetNewMappings] Buffer length too long!"));
  1279. ulBufferLength = 0x1FFFE;
  1280. }
  1281. // The ICH can only handle WORD aligned buffers.
  1282. if (PhysAddr.LowPart & 0x01)
  1283. {
  1284. // we cannot play that! Set the buffer length to 0 so
  1285. // that the HW will skip the buffer.
  1286. DOUT (DBG_WARNING, ("[GetNewMappings] Buffer address unaligned!"));
  1287. ulBufferLength = 0;
  1288. }
  1289. // The ICH cannot handle unaligned mappings with respect
  1290. // to the frame size (eg. 42 bytes on 4ch playback).
  1291. if (ulBufferLength % NumberOfChannels)
  1292. {
  1293. // modify the length (don't play the rest of the bytes)
  1294. DOUT (DBG_WARNING, ("[GetNewMappings] Buffer length unaligned!"));
  1295. ulBufferLength -= ulBufferLength % NumberOfChannels;
  1296. }
  1297. //
  1298. // Save the mapping.
  1299. //
  1300. nTail = stBDList.nTail;
  1301. stBDList.pMapData[nTail].ulTag = ulTag;
  1302. stBDList.pMapData[nTail].PhysAddr = PhysAddr;
  1303. stBDList.pMapData[nTail].pVirtAddr = VirtAddr;
  1304. stBDList.pMapData[nTail].ulBufferLength = ulBufferLength;
  1305. ulBytesMapped += ulBufferLength;
  1306. //
  1307. // Fill in the BDL entry with pointer to physical address and length.
  1308. //
  1309. stBDList.pBDEntry[nTail].dwPtrToPhyAddress = PhysAddr.LowPart;
  1310. stBDList.pBDEntry[nTail].wLength = (WORD)(ulBufferLength >> 1);
  1311. stBDList.pBDEntry[nTail].wPolicyBits = BUP_SET;
  1312. //
  1313. // Generate an interrupt when portcls tells us to or roughly every 10ms.
  1314. //
  1315. if (Flags || (ulBytesMapped > (CurrentRate * NumberOfChannels * 2) / 100))
  1316. {
  1317. stBDList.pBDEntry[nTail].wPolicyBits |= IOC_ENABLE;
  1318. ulBytesMapped = 0;
  1319. }
  1320. //
  1321. // Take the new mapping into account.
  1322. //
  1323. stBDList.nTail = (stBDList.nTail + 1) & BDL_MASK;
  1324. stBDList.nBDEntries++;
  1325. TotalBytesMapped += (ULONGLONG)ulBufferLength;
  1326. nInsertMappings++;
  1327. //
  1328. // Set last valid index (LVI) register! We need to do this here to avoid inconsistency
  1329. // of the BDList with the HW. Note that we need to release spin locks every time
  1330. // we call into portcls, that means we can be interrupted by ReleaseUsedMappings.
  1331. //
  1332. Wave->AdapterCommon->WriteBMControlRegister (m_ulBDAddr + X_LVI, (UCHAR)nTail);
  1333. }
  1334. //
  1335. // If there were processed mappings, print out some debug messages and eventually try to
  1336. // restart DMA engine.
  1337. //
  1338. if (nInsertMappings)
  1339. {
  1340. //
  1341. // Print debug information ...
  1342. //
  1343. DOUT (DBG_DMA, ("[GetNewMappings] Got %d mappings.", nInsertMappings));
  1344. DOUT (DBG_DMA, ("[GetNewMappings] Head %d, Tail %d, Entries %d.",
  1345. stBDList.nHead, stBDList.nTail, stBDList.nBDEntries));
  1346. if (DMAEngineState & DMA_ENGINE_NEED_START)
  1347. ResumeDMA ();
  1348. }
  1349. // Release the mapping spin lock
  1350. KeReleaseSpinLock (&MapLock,OldIrql);
  1351. return ntStatus;
  1352. }
  1353. /*****************************************************************************
  1354. * CMiniportWaveICHStream::ReleaseUsedMappings
  1355. *****************************************************************************
  1356. * This routine unmaps previously mapped memory that the hardware has
  1357. * completed processing on. This routine is typically called at DPC level
  1358. * from the stream deferred procedure call that results from a stream
  1359. * interrupt. The mapping spin lock must be held when calling this routine.
  1360. */
  1361. NTSTATUS CMiniportWaveICHStream::ReleaseUsedMappings (void)
  1362. {
  1363. KIRQL OldIrql;
  1364. int nMappingsReleased = 0;
  1365. DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::ReleaseUsedMappings]"));
  1366. // acquire the mapping spin lock
  1367. KeAcquireSpinLock (&MapLock,&OldIrql);
  1368. //
  1369. // Clean up everything to that index.
  1370. //
  1371. while (stBDList.nBDEntries)
  1372. {
  1373. //
  1374. // Get current index
  1375. //
  1376. int nCurrentIndex = (int)Wave->AdapterCommon->
  1377. ReadBMControlRegister8 (m_ulBDAddr + X_CIV);
  1378. //
  1379. // When CIV is == Head -1 we released all mappings.
  1380. //
  1381. if (nCurrentIndex == ((stBDList.nHead - 1) & BDL_MASK))
  1382. {
  1383. break;
  1384. }
  1385. //
  1386. // Check if CIV is between head and tail.
  1387. //
  1388. if (nCurrentIndex < stBDList.nHead)
  1389. {
  1390. //
  1391. // Check for CIV being outside range.
  1392. //
  1393. if ((nCurrentIndex + MAX_BDL_ENTRIES) >=
  1394. (stBDList.nHead + stBDList.nBDEntries))
  1395. {
  1396. DOUT (DBG_ERROR, ("[ReleaseUsedMappings] CIV out of range!"));
  1397. break;
  1398. }
  1399. }
  1400. else
  1401. {
  1402. //
  1403. // Check for CIV being outside range.
  1404. //
  1405. if (nCurrentIndex >= (stBDList.nHead + stBDList.nBDEntries))
  1406. {
  1407. DOUT (DBG_ERROR, ("[ReleaseUsedMappings] CIV out of range!"));
  1408. break;
  1409. }
  1410. }
  1411. //
  1412. // Check to see if we've released all the buffers.
  1413. //
  1414. if (stBDList.nHead == nCurrentIndex)
  1415. {
  1416. if (nCurrentIndex == ((stBDList.nTail - 1) & BDL_MASK))
  1417. {
  1418. //
  1419. // A special case is starvation or stop of stream, when the
  1420. // DMA engine finished playing the buffers, CVI is equal LVI
  1421. // and SR_CELV is set.
  1422. //
  1423. if (!(Wave->AdapterCommon->
  1424. ReadBMControlRegister16 (m_ulBDAddr + X_SR) & SR_CELV))
  1425. {
  1426. // It is still playing the last buffer.
  1427. break;
  1428. }
  1429. //
  1430. // In case the CVI=LVI bit is set, nBDEntries should be 1
  1431. //
  1432. if (stBDList.nBDEntries != 1)
  1433. {
  1434. DOUT (DBG_ERROR, ("[ReleaseUsedMappings] Inconsitency: Tail reached and Entries != 1."));
  1435. }
  1436. }
  1437. else
  1438. {
  1439. //
  1440. // Bail out. Current Index did not move.
  1441. //
  1442. break;
  1443. }
  1444. }
  1445. //
  1446. // Save the tag and remove the entry from the list.
  1447. //
  1448. ULONG ulTag = stBDList.pMapData[stBDList.nHead].ulTag;
  1449. TotalBytesReleased += (ULONGLONG)stBDList.pMapData[stBDList.nHead].ulBufferLength;
  1450. stBDList.nBDEntries--;
  1451. stBDList.nHead = (stBDList.nHead + 1) & BDL_MASK;
  1452. nMappingsReleased++;
  1453. // Release the mapping spin lock
  1454. KeReleaseSpinLock (&MapLock,OldIrql);
  1455. //
  1456. // Release this entry.
  1457. //
  1458. PortStream->ReleaseMapping ((PVOID)ULongToPtr(ulTag));
  1459. // acquire the mapping spin lock
  1460. KeAcquireSpinLock (&MapLock,&OldIrql);
  1461. }
  1462. // Print some debug information in case we released mappings.
  1463. if (nMappingsReleased)
  1464. {
  1465. //
  1466. // Print release information and return.
  1467. //
  1468. DOUT (DBG_DMA, ("[ReleaseUsedMappings] Head %d, Tail %d, Entries %d.",
  1469. stBDList.nHead, stBDList.nTail, stBDList.nBDEntries));
  1470. }
  1471. // Release the mapping spin lock
  1472. KeReleaseSpinLock (&MapLock,OldIrql);
  1473. return STATUS_SUCCESS;
  1474. }
  1475. /*****************************************************************************
  1476. * CMiniportWaveICHStream::ResetDMA
  1477. *****************************************************************************
  1478. * This routine resets the Run/Pause bit in the control register. In addition, it
  1479. * resets all DMA registers contents.
  1480. * You need to have the spin lock "MapLock" acquired.
  1481. */
  1482. NTSTATUS CMiniportWaveICHStream::ResetDMA (void)
  1483. {
  1484. DOUT (DBG_PRINT, ("ResetDMA"));
  1485. //
  1486. // Turn off DMA engine (or make sure it's turned off)
  1487. //
  1488. UCHAR RegisterValue = Wave->AdapterCommon->
  1489. ReadBMControlRegister8 (m_ulBDAddr + X_CR) & ~CR_RPBM;
  1490. Wave->AdapterCommon->
  1491. WriteBMControlRegister (m_ulBDAddr + X_CR, RegisterValue);
  1492. //
  1493. // Reset all register contents.
  1494. //
  1495. RegisterValue |= CR_RR;
  1496. Wave->AdapterCommon->
  1497. WriteBMControlRegister (m_ulBDAddr + X_CR, RegisterValue);
  1498. //
  1499. // Wait until reset condition is cleared by HW; should not take long.
  1500. //
  1501. ULONGLONG ullStartTime = PcGetTimeInterval (0);
  1502. BOOL bTimedOut = TRUE;
  1503. do
  1504. {
  1505. if (!(Wave->AdapterCommon->
  1506. ReadBMControlRegister8 (m_ulBDAddr + X_CR) & CR_RR))
  1507. {
  1508. bTimedOut = FALSE;
  1509. break;
  1510. }
  1511. } while (PcGetTimeInterval (ullStartTime) < GTI_MILLISECONDS (1000));
  1512. if (bTimedOut)
  1513. {
  1514. DOUT (DBG_ERROR, ("ResetDMA TIMEOUT!!"));
  1515. }
  1516. //
  1517. // We only want interrupts upon completion.
  1518. //
  1519. RegisterValue = CR_IOCE | CR_LVBIE;
  1520. Wave->AdapterCommon->
  1521. WriteBMControlRegister (m_ulBDAddr + X_CR, RegisterValue);
  1522. //
  1523. // Setup the Buffer Descriptor Base Address (BDBA) register.
  1524. //
  1525. Wave->AdapterCommon->
  1526. WriteBMControlRegister (m_ulBDAddr,
  1527. stBDList.PhysAddr.u.LowPart);
  1528. //
  1529. // Set the DMA engine state.
  1530. //
  1531. DMAEngineState = DMA_ENGINE_RESET;
  1532. return STATUS_SUCCESS;
  1533. }
  1534. /*****************************************************************************
  1535. * CMiniportWaveICHStream::PauseDMA
  1536. *****************************************************************************
  1537. * This routine pauses a hardware stream by reseting the Run/Pause bit in the
  1538. * control registers, leaving DMA registers content intact so that the stream
  1539. * can later be resumed.
  1540. * You need to have the spin lock "MapLock" acquired.
  1541. */
  1542. NTSTATUS CMiniportWaveICHStream::PauseDMA (void)
  1543. {
  1544. DOUT (DBG_PRINT, ("PauseDMA"));
  1545. //
  1546. // Only pause if we're actually "ON" (DMA_ENGINE_ON or DMA_ENGINE_NEED_START)
  1547. //
  1548. if (!(DMAEngineState & DMA_ENGINE_ON))
  1549. {
  1550. return STATUS_SUCCESS;
  1551. }
  1552. //
  1553. // Turn off DMA engine by resetting the RPBM bit to 0. Don't reset any
  1554. // registers.
  1555. //
  1556. UCHAR RegisterValue = Wave->AdapterCommon->
  1557. ReadBMControlRegister8 (m_ulBDAddr + X_CR);
  1558. RegisterValue &= ~CR_RPBM;
  1559. Wave->AdapterCommon->
  1560. WriteBMControlRegister (m_ulBDAddr + X_CR, RegisterValue);
  1561. //
  1562. // DMA_ENGINE_NEED_START transitions to DMA_ENGINE_RESET.
  1563. // DMA_ENGINE_ON transitions to DMA_ENGINE_OFF.
  1564. //
  1565. DMAEngineState &= DMA_ENGINE_RESET;
  1566. return STATUS_SUCCESS;
  1567. }
  1568. /*****************************************************************************
  1569. * CMiniportWaveICHStream::ResumeDMA
  1570. *****************************************************************************
  1571. * This routine sets the Run/Pause bit for the particular DMA engine to resume
  1572. * it after it's been paused. This assumes that DMA registers content have
  1573. * been preserved.
  1574. * You need to have the spin lock "MapLock" acquired.
  1575. */
  1576. NTSTATUS CMiniportWaveICHStream::ResumeDMA (void)
  1577. {
  1578. DOUT (DBG_PRINT, ("ResumeDMA"));
  1579. //
  1580. // Before we can turn on the DMA engine the first time, we need to check
  1581. // if we have at least 2 mappings in the scatter gather table.
  1582. //
  1583. if ((DMAEngineState & DMA_ENGINE_RESET) && (stBDList.nBDEntries < 2))
  1584. {
  1585. //
  1586. // That won't work. Set engine state to DMA_ENGINE_NEED_START so that
  1587. // we don't forget to call here regularly.
  1588. //
  1589. DMAEngineState = DMA_ENGINE_NEED_START;
  1590. return STATUS_SUCCESS;
  1591. }
  1592. //
  1593. // Turn DMA engine on by setting the RPBM bit to 1. Don't do anything to
  1594. // the registers.
  1595. //
  1596. UCHAR RegisterValue = Wave->AdapterCommon->
  1597. ReadBMControlRegister8 (m_ulBDAddr + X_CR) | CR_RPBM;
  1598. Wave->AdapterCommon->
  1599. WriteBMControlRegister (m_ulBDAddr + X_CR, RegisterValue);
  1600. //
  1601. // Set the DMA engine state.
  1602. //
  1603. DMAEngineState = DMA_ENGINE_ON;
  1604. return STATUS_SUCCESS;
  1605. }