📄 ichwave.cpp
字号:
* This routine is called when new mappings are available from the port driver.
* The routine places mappings into the input mapping queue. ICH can handle up
* to 32 entries (descriptors). We program the DMA registers if we have at least
* one mapping in the queue. The mapping spin lock must be held when calling
* this routine.
*/
NTSTATUS CMiniportWaveICHStream::GetNewMappings (void)
{
KIRQL OldIrql;
NTSTATUS ntStatus = STATUS_SUCCESS;
ULONG ulBytesMapped = 0;
int nInsertMappings = 0;
int nTail; // aut. variable
DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::GetNewMappings]"));
// acquire the mapping spin lock
KeAcquireSpinLock (&MapLock,&OldIrql);
#if (DBG)
if (Wave->AdapterCommon->ReadBMControlRegister16 (m_ulBDAddr + X_SR) & SR_CELV)
{
//
// We starve. :-(
//
DOUT (DBG_DMA, ("[GetNewMappings] We starved ... Head %d, Tail %d, Entries %d.",
stBDList.nHead, stBDList.nTail, stBDList.nBDEntries));
}
#endif
//
// Get available mappings up to the max of 31 that we can hold in the BDL.
//
while (stBDList.nBDEntries < (MAX_BDL_ENTRIES - 1))
{
//
// Get the information from the list.
//
ULONG Flags;
ULONG ulTag = stBDList.ulTagCounter++;
ULONG ulBufferLength;
PHYSICAL_ADDRESS PhysAddr;
PVOID VirtAddr;
// Release the mapping spin lock
KeReleaseSpinLock (&MapLock,OldIrql);
//
// Try to get the mapping from the port.
// Here comes the problem: When calling GetMapping or ReleaseMapping we
// cannot hold our spin lock. So we need to buffer the return values and
// stick the information into the structure later when we have the spin
// lock acquired.
//
ntStatus = PortStream->GetMapping ((PVOID)ULongToPtr(ulTag),
(PPHYSICAL_ADDRESS)&PhysAddr,
&VirtAddr,
&ulBufferLength,
&Flags);
// Acquire the mapping spin lock
KeAcquireSpinLock (&MapLock,&OldIrql);
//
// Quit this loop when we run out of mappings.
//
if (!NT_SUCCESS (ntStatus))
{
break;
}
// Sanity check: The audio stack will not give you data
// that you cannot handle, but an application could use
// DirectKS and send bad buffer down.
// One mapping needs to be <0x1FFFF bytes for mono
// streams on the ICH.
if (ulBufferLength > 0x1FFFE)
{
// That is a little too long. That should never happen.
DOUT (DBG_ERROR, ("[GetNewMappings] Buffer length too long!"));
ulBufferLength = 0x1FFFE;
}
// The ICH can only handle WORD aligned buffers.
if (PhysAddr.LowPart & 0x01)
{
// we cannot play that! Set the buffer length to 0 so
// that the HW will skip the buffer.
DOUT (DBG_WARNING, ("[GetNewMappings] Buffer address unaligned!"));
ulBufferLength = 0;
}
// The ICH cannot handle unaligned mappings with respect
// to the frame size (eg. 42 bytes on 4ch playback).
if (ulBufferLength % NumberOfChannels)
{
// modify the length (don't play the rest of the bytes)
DOUT (DBG_WARNING, ("[GetNewMappings] Buffer length unaligned!"));
ulBufferLength -= ulBufferLength % NumberOfChannels;
}
//
// Save the mapping.
//
nTail = stBDList.nTail;
stBDList.pMapData[nTail].ulTag = ulTag;
stBDList.pMapData[nTail].PhysAddr = PhysAddr;
stBDList.pMapData[nTail].pVirtAddr = VirtAddr;
stBDList.pMapData[nTail].ulBufferLength = ulBufferLength;
ulBytesMapped += ulBufferLength;
//
// Fill in the BDL entry with pointer to physical address and length.
//
stBDList.pBDEntry[nTail].dwPtrToPhyAddress = PhysAddr.LowPart;
stBDList.pBDEntry[nTail].wLength = (WORD)(ulBufferLength >> 1);
stBDList.pBDEntry[nTail].wPolicyBits = BUP_SET;
//
// Generate an interrupt when portcls tells us to or roughly every 10ms.
//
if (Flags || (ulBytesMapped > (CurrentRate * NumberOfChannels * 2) / 100))
{
stBDList.pBDEntry[nTail].wPolicyBits |= IOC_ENABLE;
ulBytesMapped = 0;
}
//
// Take the new mapping into account.
//
stBDList.nTail = (stBDList.nTail + 1) & BDL_MASK;
stBDList.nBDEntries++;
TotalBytesMapped += (ULONGLONG)ulBufferLength;
nInsertMappings++;
//
// Set last valid index (LVI) register! We need to do this here to avoid inconsistency
// of the BDList with the HW. Note that we need to release spin locks every time
// we call into portcls, that means we can be interrupted by ReleaseUsedMappings.
//
Wave->AdapterCommon->WriteBMControlRegister (m_ulBDAddr + X_LVI, (UCHAR)nTail);
}
//
// If there were processed mappings, print out some debug messages and eventually try to
// restart DMA engine.
//
if (nInsertMappings)
{
//
// Print debug information ...
//
DOUT (DBG_DMA, ("[GetNewMappings] Got %d mappings.", nInsertMappings));
DOUT (DBG_DMA, ("[GetNewMappings] Head %d, Tail %d, Entries %d.",
stBDList.nHead, stBDList.nTail, stBDList.nBDEntries));
if (DMAEngineState & DMA_ENGINE_NEED_START)
ResumeDMA ();
}
// Release the mapping spin lock
KeReleaseSpinLock (&MapLock,OldIrql);
return ntStatus;
}
/*****************************************************************************
* CMiniportWaveICHStream::ReleaseUsedMappings
*****************************************************************************
* This routine unmaps previously mapped memory that the hardware has
* completed processing on. This routine is typically called at DPC level
* from the stream deferred procedure call that results from a stream
* interrupt. The mapping spin lock must be held when calling this routine.
*/
NTSTATUS CMiniportWaveICHStream::ReleaseUsedMappings (void)
{
KIRQL OldIrql;
int nMappingsReleased = 0;
DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::ReleaseUsedMappings]"));
// acquire the mapping spin lock
KeAcquireSpinLock (&MapLock,&OldIrql);
//
// Clean up everything to that index.
//
while (stBDList.nBDEntries)
{
//
// Get current index
//
int nCurrentIndex = (int)Wave->AdapterCommon->
ReadBMControlRegister8 (m_ulBDAddr + X_CIV);
//
// When CIV is == Head -1 we released all mappings.
//
if (nCurrentIndex == ((stBDList.nHead - 1) & BDL_MASK))
{
break;
}
//
// Check if CIV is between head and tail.
//
if (nCurrentIndex < stBDList.nHead)
{
//
// Check for CIV being outside range.
//
if ((nCurrentIndex + MAX_BDL_ENTRIES) >=
(stBDList.nHead + stBDList.nBDEntries))
{
DOUT (DBG_ERROR, ("[ReleaseUsedMappings] CIV out of range!"));
break;
}
}
else
{
//
// Check for CIV being outside range.
//
if (nCurrentIndex >= (stBDList.nHead + stBDList.nBDEntries))
{
DOUT (DBG_ERROR, ("[ReleaseUsedMappings] CIV out of range!"));
break;
}
}
//
// Check to see if we've released all the buffers.
//
if (stBDList.nHead == nCurrentIndex)
{
if (nCurrentIndex == ((stBDList.nTail - 1) & BDL_MASK))
{
//
// A special case is starvation or stop of stream, when the
// DMA engine finished playing the buffers, CVI is equal LVI
// and SR_CELV is set.
//
if (!(Wave->AdapterCommon->
ReadBMControlRegister16 (m_ulBDAddr + X_SR) & SR_CELV))
{
// It is still playing the last buffer.
break;
}
//
// In case the CVI=LVI bit is set, nBDEntries should be 1
//
if (stBDList.nBDEntries != 1)
{
DOUT (DBG_ERROR, ("[ReleaseUsedMappings] Inconsitency: Tail reached and Entries != 1."));
}
}
else
{
//
// Bail out. Current Index did not move.
//
break;
}
}
//
// Save the tag and remove the entry from the list.
//
ULONG ulTag = stBDList.pMapData[stBDList.nHead].ulTag;
TotalBytesReleased += (ULONGLONG)stBDList.pMapData[stBDList.nHead].ulBufferLength;
stBDList.nBDEntries--;
stBDList.nHead = (stBDList.nHead + 1) & BDL_MASK;
nMappingsReleased++;
// Release the mapping spin lock
KeReleaseSpinLock (&MapLock,OldIrql);
//
// Release this entry.
//
PortStream->ReleaseMapping ((PVOID)ULongToPtr(ulTag));
// acquire the mapping spin lock
KeAcquireSpinLock (&MapLock,&OldIrql);
}
// Print some debug information in case we released mappings.
if (nMappingsReleased)
{
//
// Print release information and return.
//
DOUT (DBG_DMA, ("[ReleaseUsedMappings] Head %d, Tail %d, Entries %d.",
stBDList.nHead, stBDList.nTail, stBDList.nBDEntries));
}
// Release the mapping spin lock
KeReleaseSpinLock (&MapLock,OldIrql);
return STATUS_SUCCESS;
}
/*****************************************************************************
* CMiniportWaveICHStream::ResetDMA
*****************************************************************************
* This routine resets the Run/Pause bit in the control register. In addition, it
* resets all DMA registers contents.
* You need to have the spin lock "MapLock" acquired.
*/
NTSTATUS CMiniportWaveICHStream::ResetDMA (void)
{
DOUT (DBG_PRINT, ("ResetDMA"));
//
// Turn off DMA engine (or make sure it's turned off)
//
UCHAR RegisterValue = Wave->AdapterCommon->
ReadBMControlRegister8 (m_ulBDAddr + X_CR) & ~CR_RPBM;
Wave->AdapterCommon->
WriteBMControlRegister (m_ulBDAddr + X_CR, RegisterValue);
//
// Reset all register contents.
//
RegisterValue |= CR_RR;
Wave->AdapterCommon->
WriteBMControlRegister (m_ulBDAddr + X_CR, RegisterValue);
//
// Wait until reset condition is cleared by HW; should not take long.
//
ULONGLONG ullStartTime = PcGetTimeInterval (0);
BOOL bTimedOut = TRUE;
do
{
if (!(Wave->AdapterCommon->
ReadBMControlRegister8 (m_ulBDAddr + X_CR) & CR_RR))
{
bTimedOut = FALSE;
break;
}
} while (PcGetTimeInterval (ullStartTime) < GTI_MILLISECONDS (1000));
if (bTimedOut)
{
DOUT (DBG_ERROR, ("ResetDMA TIMEOUT!!"));
}
//
// We only want interrupts upon completion.
//
RegisterValue = CR_IOCE | CR_LVBIE;
Wave->AdapterCommon->
WriteBMControlRegister (m_ulBDAddr + X_CR, RegisterValue);
//
// Setup the Buffer Descriptor Base Address (BDBA) register.
//
Wave->AdapterCommon->
WriteBMControlRegister (m_ulBDAddr,
stBDList.PhysAddr.u.LowPart);
//
// Set the DMA engine state.
//
DMAEngineState = DMA_ENGINE_RESET;
return STATUS_SUCCESS;
}
/*****************************************************************************
* CMiniportWaveICHStream::PauseDMA
*****************************************************************************
* This routine pauses a hardware stream by reseting the Run/Pause bit in the
* control registers, leaving DMA registers content intact so that the stream
* can later be resumed.
* You need to have the spin lock "MapLock" acquired.
*/
NTSTATUS CMiniportWaveICHStream::PauseDMA (void)
{
DOUT (DBG_PRINT, ("PauseDMA"));
//
// Only pause if we're actually "ON" (DMA_ENGINE_ON or DMA_ENGINE_NEED_START)
//
if (!(DMAEngineState & DMA_ENGINE_ON))
{
return STATUS_SUCCESS;
}
//
// Turn off DMA engine by resetting the RPBM bit to 0. Don't reset any
// registers.
//
UCHAR RegisterValue = Wave->AdapterCommon->
ReadBMControlRegister8 (m_ulBDAddr + X_CR);
RegisterValue &= ~CR_RPBM;
Wave->AdapterCommon->
WriteBMControlRegister (m_ulBDAddr + X_CR, RegisterValue);
//
// DMA_ENGINE_NEED_START transitions to DMA_ENGINE_RESET.
// DMA_ENGINE_ON transitions to DMA_ENGINE_OFF.
//
DMAEngineState &= DMA_ENGINE_RESET;
return STATUS_SUCCESS;
}
/*****************************************************************************
* CMiniportWaveICHStream::ResumeDMA
*****************************************************************************
* This routine sets the Run/Pause bit for the particular DMA engine to resume
* it after it's been paused. This assumes that DMA registers content have
* been preserved.
* You need to have the spin lock "MapLock" acquired.
*/
NTSTATUS CMiniportWaveICHStream::ResumeDMA (void)
{
DOUT (DBG_PRINT, ("ResumeDMA"));
//
// Before we can turn on the DMA engine the first time, we need to check
// if we have at least 2 mappings in the scatter gather table.
//
if ((DMAEngineState & DMA_ENGINE_RESET) && (stBDList.nBDEntries < 2))
{
//
// That won't work. Set engine state to DMA_ENGINE_NEED_START so that
// we don't forget to call here regularly.
//
DMAEngineState = DMA_ENGINE_NEED_START;
return STATUS_SUCCESS;
}
//
// Turn DMA engine on by setting the RPBM bit to 1. Don't do anything to
// the registers.
//
UCHAR RegisterValue = Wave->AdapterCommon->
ReadBMControlRegister8 (m_ulBDAddr + X_CR) | CR_RPBM;
Wave->AdapterCommon->
WriteBMControlRegister (m_ulBDAddr + X_CR, RegisterValue);
//
// Set the DMA engine state.
//
DMAEngineState = DMA_ENGINE_ON;
return STATUS_SUCCESS;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -