📄 ichwave.cpp
字号:
/*****************************************************************************
* CMiniportWaveICHStream::Service
*****************************************************************************
* This routine is called by the port driver in response to the interrupt
* service routine requesting service on the stream's service group.
* Requesting service on the service group results in a DPC being scheduled
* that calls this routine when it runs.
*/
STDMETHODIMP_(void) CMiniportWaveICHStream::Service (void)
{
DOUT (DBG_PRINT, ("Service"));
// release all mappings
ReleaseUsedMappings ();
// get new mappings
GetNewMappings ();
}
/*****************************************************************************
* CMiniportWaveICHStream::NormalizePhysicalPosition
*****************************************************************************
* Given a physical position based on the actual number of bytes transferred,
* this function converts the position to a time-based value of 100ns units.
*/
STDMETHODIMP_(NTSTATUS) CMiniportWaveICHStream::NormalizePhysicalPosition
(
IN OUT PLONGLONG PhysicalPosition
)
{
ULONG SampleSize;
DOUT (DBG_PRINT, ("NormalizePhysicalPosition"));
//
// Determine the sample size in bytes
//
SampleSize = DataFormat->WaveFormatEx.nChannels * 2;
//
// Calculate the time in 100ns steps.
//
*PhysicalPosition = (_100NS_UNITS_PER_SECOND / SampleSize *
*PhysicalPosition) / CurrentRate;
return STATUS_SUCCESS;
}
/*****************************************************************************
* CMiniportWaveICHStream::GetPosition
*****************************************************************************
* Gets the stream position. This is a byte count of the current position of
* a stream running on a particular DMA engine. We must return a sample
* accurate count or the WaveDrv32 wave drift tests (35.2 & 36.2) will fail.
*
* The position is the sum of three parts:
* 1) The total number of bytes in released buffers
* 2) The position in the current buffer.
* 3) The total number of bytes in played but not yet released buffers
*/
STDMETHODIMP_(NTSTATUS) CMiniportWaveICHStream::GetPosition
(
OUT PULONGLONG Position
)
{
KIRQL OldIrql;
UCHAR nCurrentIndex = 0;
DWORD RegisterX_PICB;
ASSERT (Position);
//
// Acquire the mapping spin lock.
//
KeAcquireSpinLock (&MapLock, &OldIrql);
//
// Start with TotalBytesReleased (mappings released).
//
*Position = TotalBytesReleased;
//
// If we have entries in the list, we may have buffers that have not been
// released but have been at least partially played.
//
if (stBDList.nBDEntries)
{
//
// Repeat this until we get the same reading twice. This will prevent
// jumps when we are near the end of the buffer.
//
do
{
nCurrentIndex = Wave->AdapterCommon->
ReadBMControlRegister8 (m_ulBDAddr + X_CIV);
RegisterX_PICB = (DWORD)Wave->AdapterCommon->ReadBMControlRegister16 (m_ulBDAddr + X_PICB);
} while (nCurrentIndex != (int)Wave->AdapterCommon->
ReadBMControlRegister8 (m_ulBDAddr + X_CIV));
//
// If we never released a buffer and register X_PICB is zero then the DMA was not
// initialized yet and the position should be zero.
//
if (RegisterX_PICB || nCurrentIndex || TotalBytesReleased)
{
//
// Add in our position in the current buffer. The read returns the
// amount left in the buffer.
//
*Position += (stBDList.pMapData[nCurrentIndex].ulBufferLength - (RegisterX_PICB << 1));
//
// Total any buffers that have been played and not released.
//
if (nCurrentIndex != ((stBDList.nHead -1) & BDL_MASK))
{
int i = stBDList.nHead;
while (i != nCurrentIndex)
{
*Position += (ULONGLONG)stBDList.pMapData[i].ulBufferLength;
i = (i + 1) & BDL_MASK;
}
}
}
}
DOUT (DBG_POSITION, ("[GetPosition] POS: %08x'%08x\n", (DWORD)(*Position >> 32), (DWORD)*Position));
//
// Release the mapping spin lock.
//
KeReleaseSpinLock (&MapLock, OldIrql);
return STATUS_SUCCESS;
}
/*****************************************************************************
* CMiniportWaveICHStream::RevokeMappings
*****************************************************************************
* This routine is used by the port to revoke mappings previously delivered
* to the miniport stream that have not yet been unmapped. This would
* typically be called in response to an I/O cancellation request.
*/
STDMETHODIMP_(NTSTATUS) CMiniportWaveICHStream::RevokeMappings
(
IN PVOID FirstTag,
IN PVOID LastTag,
OUT PULONG MappingsRevoked
)
{
ASSERT (MappingsRevoked);
KIRQL OldIrql;
ULONG ulOldDMAEngineState;
int nCurrentIndex, nSearchIndex, nFirst, nLast, nNumMappings;
DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::RevokeMappings]"));
//
// print information about the scatter gather list.
//
DOUT (DBG_DMA, ("Head %d, Tail %d, Tag counter %d, Entries %d.",
stBDList.nHead, stBDList.nTail, stBDList.ulTagCounter,
stBDList.nBDEntries));
//
// Start accessing the mappings.
//
KeAcquireSpinLock (&MapLock, &OldIrql);
//
// Save old DMA engine state.
//
ulOldDMAEngineState = DMAEngineState;
//
// First stop the DMA engine so it won't process the next buffer in the
// scatter gather list which might be one of the revoked buffers.
//
PauseDMA ();
// Get current index
nCurrentIndex = Wave->AdapterCommon->
ReadBMControlRegister8 (m_ulBDAddr + X_CIV);
//
// We always rearrange the scatter gather list. That means we reset the DMA
// engine and move the BD list so that the entry where the current index
// pointed to is located at position 0.
//
ResetDMA ();
//
// Return immediately if we just have 1 entry in our BD list.
//
if (!stBDList.nBDEntries || (stBDList.nBDEntries == 1))
{
*MappingsRevoked = stBDList.nBDEntries;
stBDList.nHead = stBDList.nTail = stBDList.nBDEntries = 0;
//
// CIV and LVI of DMA registers are set to 0 already.
//
KeReleaseSpinLock (&MapLock, OldIrql);
return STATUS_SUCCESS;
}
//
// First move the BD list to the beginning. In case the DMA engine was
// stopped, current index may point to an empty BD entry.
//
if ((nCurrentIndex == ((stBDList.nHead - 1) & BDL_MASK)) &&
(stBDList.nBDEntries != MAX_BDL_ENTRIES - 1))
{
nCurrentIndex = stBDList.nHead; // point to head
}
//
// Move BD list to (0-((current - head) & mask)) & mask
// where ((current - head) & mask) is the difference between head and
// current index, no matter where they are :)
//
MoveBDList (stBDList.nHead, (stBDList.nTail - 1) & BDL_MASK,
(0 - ((nCurrentIndex - stBDList.nHead) & BDL_MASK)) & BDL_MASK);
//
// Update structure.
//
stBDList.nHead = (0 - ((nCurrentIndex - stBDList.nHead) & BDL_MASK)) &
BDL_MASK;
stBDList.nTail = (stBDList.nHead + stBDList.nBDEntries) & BDL_MASK;
//
// Then we have to search for the tags. If we wouldn't have to rearrange the
// scatter gather list all the time, then we could use the tag as an index
// to the array, but the only way to clear DMA caches is a reset which has
// the side-effect that we have to rearrange the BD list (see above).
//
// search for first...
//
nSearchIndex = stBDList.nHead;
do
{
if ((void *)ULongToPtr(stBDList.pMapData[nSearchIndex].ulTag) == FirstTag)
break;
nSearchIndex = (nSearchIndex + 1) & BDL_MASK;
} while (nSearchIndex != stBDList.nTail);
nFirst = nSearchIndex;
//
// Search for last...
//
nSearchIndex = stBDList.nHead;
do
{
if ((void *)ULongToPtr(stBDList.pMapData[nSearchIndex].ulTag) == LastTag)
break;
nSearchIndex = (nSearchIndex + 1) & BDL_MASK;
} while (nSearchIndex != stBDList.nTail);
nLast = nSearchIndex;
//
// Check search result.
//
if ((nFirst == stBDList.nTail) || (nLast == stBDList.nTail))
{
DOUT (DBG_ERROR, ("!!! Entry not found !!!"));
//
// restart DMA in case it was running
//
if ((ulOldDMAEngineState & DMA_ENGINE_ON) && stBDList.nBDEntries)
ResumeDMA ();
*MappingsRevoked = 0;
KeReleaseSpinLock (&MapLock, OldIrql);
return STATUS_UNSUCCESSFUL; // one of the tags not found
}
// Print the index numbers found.
DOUT (DBG_DMA, ("Removing entries %d (%d) to %d (%d).", nFirst, FirstTag,
nLast, LastTag));
//
// Calculate the entries between the indizes.
//
if (nLast < nFirst)
{
nNumMappings = ((nLast + MAX_BDL_ENTRIES) - nFirst) + 1;
}
else
{
nNumMappings = (nLast - nFirst) + 1;
}
//
// Print debug inormation.
//
DOUT (DBG_DMA, ("Found entries: %d-%d, %d entries.", nFirst, nLast,
nNumMappings));
//
// Now remove the revoked buffers. Move the BD list and modify the
// status information.
//
if (nFirst < stBDList.nTail)
{
//
// In this case, both first and last are >= the current index (0)
//
if (nLast != ((stBDList.nTail - 1) & BDL_MASK))
{
//
// Not the last entry, so move the BD list + mappings.
//
MoveBDList ((nLast + 1) & BDL_MASK, (stBDList.nTail - 1) & BDL_MASK,
nFirst);
}
stBDList.nTail = (stBDList.nTail - nNumMappings) & BDL_MASK;
}
//
// In this case, at least first is "<" than current index (0)
//
else
{
//
// Check for last.
//
if (nLast < stBDList.nTail)
{
//
// Last is ">=" current index and first is "<" current index (0).
// Remove MAX_DBL_ENTRIES - first entries in front of current index.
//
if (nFirst != stBDList.nHead)
{
//
// Move from head towards current index.
//
MoveBDList (stBDList.nHead, nFirst - 1,
(stBDList.nHead + (MAX_BDL_ENTRIES - nFirst)) &
BDL_MASK);
}
//
// Adjust head.
//
stBDList.nHead = (stBDList.nHead + (MAX_BDL_ENTRIES - nFirst)) &
BDL_MASK;
//
// Remove nLast entries from CIV to tail.
//
if (nLast != ((stBDList.nTail - 1) & BDL_MASK))
{
//
// Not the last entry, so move the BD list + mappings.
//
MoveBDList (nLast + 1, (stBDList.nTail - 1) & BDL_MASK, 0);
}
//
// Adjust tail.
//
stBDList.nTail = (stBDList.nTail - (nLast + 1)) & BDL_MASK;
}
//
// Last is "<" current index and first is "<" current index (0).
//
else
{
//
// Remove nNumMappings entries in front of current index.
//
if (nFirst != stBDList.nHead)
{
//
// Move from head towards current index.
//
MoveBDList (stBDList.nHead, nFirst - 1,
(nLast - nNumMappings) + 1);
}
//
// Adjust head.
//
stBDList.nHead = (stBDList.nHead + nNumMappings) & BDL_MASK;
}
}
//
// In all cases, reduce the number of mappings.
//
stBDList.nBDEntries -= nNumMappings;
//
// Print debug information.
//
DOUT (DBG_DMA, ("Number of mappings is now %d, Head is %d, Tail is %d",
stBDList.nBDEntries, stBDList.nHead, stBDList.nTail));
//
// Reprogram the last valid index only when tail != 0
//
if (stBDList.nTail)
{
Wave->AdapterCommon->WriteBMControlRegister (m_ulBDAddr + X_LVI,
(UCHAR)(stBDList.nTail - 1 & BDL_MASK));
}
//
// Just un-pause the DMA engine if it was running before and there are
// still entries left and tail != 0.
//
if ((ulOldDMAEngineState & DMA_ENGINE_ON) && stBDList.nBDEntries
&& stBDList.nTail)
{
ResumeDMA ();
}
//
// Release the mapping spin lock and return the number of mappings we
// revoked.
//
KeReleaseSpinLock (&MapLock, OldIrql);
*MappingsRevoked = nNumMappings;
return STATUS_SUCCESS;
}
/*****************************************************************************
* CMiniportWaveICHStream::MappingAvailable
*****************************************************************************
* This routine is called by the port driver to notify the stream that there
* are new mappings available. Note that this is ONLY called after the stream
* has previously had a GetMapping() call fail due to lack of available
* mappings.
*/
STDMETHODIMP_(void) CMiniportWaveICHStream::MappingAvailable (void)
{
DOUT (DBG_PRINT, ("MappingAvailable"));
//
// Release processed mappings.
//
ReleaseUsedMappings ();
//
// Process the new mappings.
//
GetNewMappings ();
}
/*****************************************************************************
* CMiniportWaveICHStream::GetNewMappings
*****************************************************************************
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -