📄 ichwave.cpp
字号:
PAGED_CODE ();
ASSERT (Format);
ULONG TempRate;
DWORD dwControlReg;
DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::SetFormat]"));
//
// Change sample rate when we are in the stop or pause states - not
// while running!
//
if (DMAEngineState & DMA_ENGINE_ON)
{
return STATUS_UNSUCCESSFUL;
}
//
// Ensure format falls in proper range and is supported.
//
NTSTATUS ntStatus = Wave->TestDataFormat (Format, (WavePins)(Channel << 1));
if (!NT_SUCCESS (ntStatus))
return ntStatus;
//
// Retrieve wave format portion.
//
PWAVEFORMATPCMEX waveFormat = (PWAVEFORMATPCMEX)(Format + 1);
//
// Save current rate in this context.
//
TempRate = waveFormat->Format.nSamplesPerSec;
//
// Check if we have a codec with one sample rate converter and there are streams
// already open.
//
if (Wave->Streams[PIN_WAVEIN_OFFSET] && Wave->Streams[PIN_WAVEOUT_OFFSET] &&
!Wave->AdapterCommon->GetNodeConfig (NODEC_PCM_VSR_INDEPENDENT_RATES))
{
//
// Figure out at which sample rate the other stream is running.
//
ULONG ulFrequency;
if (Wave->Streams[PIN_WAVEIN_OFFSET] == this)
ulFrequency = Wave->Streams[PIN_WAVEOUT_OFFSET]->CurrentRate;
else
ulFrequency = Wave->Streams[PIN_WAVEIN_OFFSET]->CurrentRate;
//
// Check if this sample rate is requested sample rate.
//
if (ulFrequency != TempRate)
{
return STATUS_UNSUCCESSFUL;
}
}
//
// Program the ICH to support n channels.
//
if (Channel == PIN_WAVEOUT_OFFSET)
{
dwControlReg = Wave->AdapterCommon->ReadBMControlRegister32 (GLOB_CNT);
dwControlReg = (dwControlReg & 0x03F) |
(((waveFormat->Format.nChannels >> 1) - 1) * GLOB_CNT_PCM4);
Wave->AdapterCommon->WriteBMControlRegister (GLOB_CNT, dwControlReg);
}
//
// Check for rate support by hardware. If it is supported, then update
// hardware registers else return not implemented and audio stack will
// handle it.
//
if (Capture)
{
if (Channel == PIN_WAVEIN_OFFSET)
{
ntStatus = Wave->AdapterCommon->
ProgramSampleRate (AC97REG_RECORD_SAMPLERATE, TempRate);
}
else
{
ntStatus = Wave->AdapterCommon->
ProgramSampleRate (AC97REG_MIC_SAMPLERATE, TempRate);
}
}
else
{
//
// In the playback case we might need to update several DACs
// with the new sample rate.
//
ntStatus = Wave->AdapterCommon->
ProgramSampleRate (AC97REG_FRONT_SAMPLERATE, TempRate);
if (Wave->AdapterCommon->GetNodeConfig (NODEC_SURROUND_DAC_PRESENT))
{
ntStatus = Wave->AdapterCommon->
ProgramSampleRate (AC97REG_SURROUND_SAMPLERATE, TempRate);
}
if (Wave->AdapterCommon->GetNodeConfig (NODEC_LFE_DAC_PRESENT))
{
ntStatus = Wave->AdapterCommon->
ProgramSampleRate (AC97REG_LFE_SAMPLERATE, TempRate);
}
}
if (NT_SUCCESS (ntStatus))
{
//
// print information and save the format information.
//
DataFormat = (PKSDATAFORMAT_WAVEFORMATEX)Format;
CurrentRate = TempRate;
NumberOfChannels = waveFormat->Format.nChannels;
}
return ntStatus;
}
/*****************************************************************************
* CMiniportWaveICHStream::SetContentId
*****************************************************************************
* This routine gets called by drmk.sys to pass the content to the driver.
* The driver has to enforce the rights passed.
*/
STDMETHODIMP_(NTSTATUS) CMiniportWaveICHStream::SetContentId
(
IN ULONG contentId,
IN PCDRMRIGHTS drmRights
)
{
PAGED_CODE ();
DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::SetContentId]"));
//
// If "drmRights->DigitalOutputDisable" is set, we need to disable S/P-DIF.
// Currently, we don't have knowledge about the S/P-DIF interface. However,
// in case you expanded the driver with S/P-DIF features you need to disable
// S/P-DIF or fail SetContentId. If you have HW that has S/P-DIF turned on
// by default and you don't know how to turn off (or you cannot do that)
// then you must fail SetContentId.
//
// In our case, we assume the codec has no S/P-DIF or disabled S/P-DIF by
// default, so we can ignore the flag.
//
// Store the copyright flag. We have to disable PCM recording if it's set.
//
if (!Wave->AdapterCommon->GetMiniportTopology ())
{
DOUT (DBG_ERROR, ("Topology pointer not set!"));
return STATUS_UNSUCCESSFUL;
}
else
{
Wave->AdapterCommon->GetMiniportTopology ()->
SetCopyProtectFlag (drmRights->CopyProtect);
}
//
// We assume that if we can enforce the rights, that the old content
// will be destroyed. We don't need to store the content id since we
// have only one playback channel, so we are finished here.
//
return STATUS_SUCCESS;
}
/*****************************************************************************
* Non paged code begins here
*****************************************************************************
*/
#pragma code_seg()
/*****************************************************************************
* CMiniportWaveICHStream::PowerChangeNotify
*****************************************************************************
* This functions saves and maintains the stream state through power changes.
*/
NTSTATUS CMiniportWaveICHStream::PowerChangeNotify
(
IN POWER_STATE NewState
)
{
PAGED_CODE ();
KIRQL OldIrql;
NTSTATUS ntStatus = STATUS_SUCCESS;
DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::PowerChangeNotify]"));
//
// We don't have to check the power state, that's already done by the wave
// miniport.
//
DOUT (DBG_POWER, ("Changing state to D%d.",
(ULONG)NewState.DeviceState - (ULONG)PowerDeviceD0));
switch (NewState.DeviceState)
{
case PowerDeviceD0:
//
// If we are coming from D2 or D3 we have to restore the registers cause
// there might have been a power loss.
//
if ((m_PowerState == PowerDeviceD3) || (m_PowerState == PowerDeviceD2))
{
//
// The scatter gather list is already arranged. A reset of the DMA
// brings all pointers to the default state. From there we can start.
//
// Acquire the mapping spin lock
KeAcquireSpinLock (&MapLock,&OldIrql);
ntStatus = ResetDMA ();
// Restore the remaining DMA registers, that is last valid index
// only if the index is not pointing to 0. Note that the index is
// equal to head + entries.
if (stBDList.nTail)
{
Wave->AdapterCommon->WriteBMControlRegister (m_ulBDAddr + X_LVI,
(UCHAR)((stBDList.nTail - 1) & BDL_MASK));
}
// Release the mapping spin lock
KeReleaseSpinLock (&MapLock,OldIrql);
}
break;
case PowerDeviceD1:
// Here we do nothing. The device has still enough power to keep all
// it's register values.
break;
case PowerDeviceD2:
case PowerDeviceD3:
//
// If we power down to D2 or D3 we might loose power, so we have to be
// aware of the DMA engine resetting. In that case a play would start
// with scatter gather entry 0 (the current index is read only).
// We just rearrange the scatter gather list (like we do on
// RevokeMappings) so that the current buffer which is played is at
// entry 0.
//
// Acquire the mapping spin lock
KeAcquireSpinLock (&MapLock,&OldIrql);
// Disable interrupts and stop DMA just in case.
Wave->AdapterCommon->WriteBMControlRegister (m_ulBDAddr + X_CR, (UCHAR)0);
// Get current index
int nCurrentIndex = (int)Wave->AdapterCommon->
ReadBMControlRegister8 (m_ulBDAddr + X_CIV);
//
// First move the BD list to the beginning.
//
// In case the DMA engine was stopped, current index may point to an
// empty BD entry. When we start the DMA engine it would then play this
// (undefined) entry, so we check here for that condition.
//
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;
// release the mapping spin lock
KeReleaseSpinLock (&MapLock,OldIrql);
break;
}
//
// Save the new state. This local value is used to determine when to
// cache property accesses and when to permit the driver from accessing
// the hardware.
//
m_PowerState = NewState.DeviceState;
DOUT (DBG_POWER, ("Entering D%d",
(ULONG)m_PowerState - (ULONG)PowerDeviceD0));
return ntStatus;
}
/*****************************************************************************
* CMiniportWaveICHStream::SetState
*****************************************************************************
* This routine sets/changes the DMA engine state to play, stop, or pause
*/
STDMETHODIMP_(NTSTATUS) CMiniportWaveICHStream::SetState
(
IN KSSTATE State
)
{
PAGED_CODE (); // Gets called at PASSIVE_LEVEL
KIRQL OldIrql;
DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::SetState]"));
DOUT (DBG_STREAM, ("SetState to %d", State));
//
// Start or stop the DMA engine dependent of the state.
//
switch (State)
{
case KSSTATE_STOP:
// acquire the mapping spin lock
KeAcquireSpinLock (&MapLock,&OldIrql);
// Just pause DMA. If we have mappings left in our queue,
// portcls will call RevokeMappings to destroy them.
PauseDMA ();
// release the mapping spin lock
KeReleaseSpinLock (&MapLock,OldIrql);
// Release processed mappings
ReleaseUsedMappings ();
// Reset the position counters.
TotalBytesMapped = TotalBytesReleased = 0;
break;
case KSSTATE_ACQUIRE:
case KSSTATE_PAUSE:
// acquire the mapping spin lock
KeAcquireSpinLock (&MapLock,&OldIrql);
// pause now.
PauseDMA ();
// release the mapping spin lock
KeReleaseSpinLock (&MapLock,OldIrql);
// Release processed mappings
ReleaseUsedMappings ();
break;
case KSSTATE_RUN:
//
// Let's rock.
//
// Make sure we are not running already.
if (DMAEngineState & DMA_ENGINE_ON)
{
return STATUS_SUCCESS;
}
// Release processed mappings.
ReleaseUsedMappings ();
// Get new mappings.
GetNewMappings ();
// acquire the mapping spin lock
KeAcquireSpinLock (&MapLock,&OldIrql);
// Kick DMA again just in case.
ResumeDMA ();
// release the mapping spin lock
KeReleaseSpinLock (&MapLock,OldIrql);
break;
}
return STATUS_SUCCESS;
}
/*****************************************************************************
* CMiniportWaveICHStream::MoveBDList
*****************************************************************************
* Moves the BDList.
* This function is used to remove entries from the scatter gather list or to
* move the valid entries to the top. The mapping table which is hard linked
* to the scatter gather entries is moved too.
* The function does not change any variables in tBDList.
* The mapping spin lock must be held when calling this routine.
* We use this function to remove mappings (RevokeMappings) or to rearrange the
* list for powerdown/up management (the DMA starts at position zero again).
* Note that there is a simple way of doing this also. When you zero the buffer
* length in the scatter gather, the DMA engine ignores the entry and continues
* with the next. But our way is more generic and if you ever want to port the
* driver to another DMA engine you might be thankful for this code.
*/
void CMiniportWaveICHStream::MoveBDList
(
IN int nFirst,
IN int nLast,
IN int nNewPos
)
{
DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::MoveBDList]"));
//
// Print information about the scatter gather list.
//
DOUT (DBG_DMA, ("Moving BD entry %d-%d to %d.", nFirst, nLast, nNewPos));
//
// First copy the tables to a save place.
//
RtlCopyMemory ((PVOID)stBDList.pBDEntryBackup,
(PVOID)stBDList.pBDEntry,
sizeof (tBDEntry) * MAX_BDL_ENTRIES);
RtlCopyMemory ((PVOID)stBDList.pMapDataBackup,
(PVOID)stBDList.pMapData,
sizeof (tMapData) * MAX_BDL_ENTRIES);
//
// We move all the entries in blocks to the new position.
//
int nBlockCounter = 0;
do
{
nBlockCounter++;
//
// We must copy the block when the index wraps around (ring buffer)
// or we are at the last entry.
//
if (((nNewPos + nBlockCounter) == MAX_BDL_ENTRIES) || // wrap around
((nFirst + nBlockCounter) == MAX_BDL_ENTRIES) || // wrap around
((nFirst + nBlockCounter) == (nLast + 1))) // last entry
{
//
// copy one block (multiple entries).
//
RtlCopyMemory ((PVOID)&stBDList.pBDEntry[nNewPos],
(PVOID)&stBDList.pBDEntryBackup[nFirst],
sizeof (tBDEntry) * nBlockCounter);
RtlCopyMemory ((PVOID)&stBDList.pMapData[nNewPos],
(PVOID)&stBDList.pMapDataBackup[nFirst],
sizeof (tMapData) * nBlockCounter);
// adjust the index
nNewPos = (nNewPos + nBlockCounter) & BDL_MASK;
nFirst = (nFirst + nBlockCounter) & BDL_MASK;
nBlockCounter = 0;
}
// nBlockCounter should be zero when the end condition hits.
} while (((nFirst + nBlockCounter - 1) & BDL_MASK) != nLast);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -