📄 ichwave.cpp
字号:
/********************************************************************************
** Copyright (c) 1998-2000 Microsoft Corporation. All Rights Reserved.
**
** Portions Copyright (c) 1998-1999 Intel Corporation
**
********************************************************************************/
// Every debug output has "Modulname text"
static char STR_MODULENAME[] = "ICH Stream: ";
#include "minwave.h"
#include "ichwave.h"
/*****************************************************************************
* General Info
*****************************************************************************
* To protect the stBDList structure that is used to store mappings, we use a
* spin lock called MapLock. This spin lock is also acquired when we change
* the DMA registers. Normally, changes in stBDList and the DMA registers go
* hand in hand. In case we only want to change the DMA registers, we need
* to acquire the spin lock!
*/
#pragma code_seg("PAGE")
/*****************************************************************************
* CreateMiniportWaveICHStream
*****************************************************************************
* Creates a wave miniport stream object for the ICH audio adapter. This is
* (nearly) like the macro STD_CREATE_BODY_ from STDUNK.H.
*/
NTSTATUS CreateMiniportWaveICHStream
(
OUT CMiniportWaveICHStream **WaveIchStream,
IN PUNKNOWN pUnknownOuter,
IN POOL_TYPE PoolType
)
{
PAGED_CODE ();
DOUT (DBG_PRINT, ("[CreateMiniportWaveICHStream]"));
//
// This is basically like the macro at stdunk with the change that we
// don't cast to interface unknown but to interface WaveIchStream.
//
*WaveIchStream = new (PoolType, 'rCcP')
CMiniportWaveICHStream (pUnknownOuter);
if (*WaveIchStream)
{
(*WaveIchStream)->AddRef ();
return STATUS_SUCCESS;
}
return STATUS_INSUFFICIENT_RESOURCES;
}
/*****************************************************************************
* CMiniportWaveICHStream::~CMiniportWaveICHStream
*****************************************************************************
* Destructor
*/
CMiniportWaveICHStream::~CMiniportWaveICHStream ()
{
PAGED_CODE ();
DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::~CMiniportWaveICHStream]"));
//
// 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));
if (Wave)
{
//
// Disable interrupts and stop DMA just in case.
//
if (Wave->AdapterCommon)
{
Wave->AdapterCommon->WriteBMControlRegister (m_ulBDAddr + X_CR, (UCHAR)0);
//
// Update also the topology miniport if this was the render stream.
//
if (Wave->AdapterCommon->GetMiniportTopology () &&
(Channel == PIN_WAVEOUT_OFFSET))
{
Wave->AdapterCommon->GetMiniportTopology ()->SetCopyProtectFlag (FALSE);
}
}
//
// Remove stream from miniport Streams array.
//
if (Wave->Streams[Channel] == this)
{
Wave->Streams[Channel] = NULL;
}
//
// Release the scatter/gather table.
//
if (stBDList.pBDEntry)
{
HalFreeCommonBuffer (Wave->AdapterObject,
PAGE_SIZE,
stBDList.PhysAddr,
(PVOID)stBDList.pBDEntry,
FALSE);
stBDList.pBDEntry = NULL;
}
//
// Release the miniport.
//
Wave->Release ();
Wave = NULL;
}
//
// Release the service group.
//
if (ServiceGroup)
{
ServiceGroup->Release ();
ServiceGroup = NULL;
}
//
// Release the mapping table.
//
if (stBDList.pMapData)
{
ExFreePool (stBDList.pMapData);
stBDList.pMapData = NULL;
}
//
// Release the port stream.
//
if (PortStream)
{
PortStream->Release ();
PortStream = NULL;
}
}
/*****************************************************************************
* CMiniportWaveICHStream::Init
*****************************************************************************
* This routine initializes the stream object, sets up the BDL, and programs
* the buffer descriptor list base address register for the pin being
* initialized.
*/
NTSTATUS CMiniportWaveICHStream::Init
(
IN CMiniportWaveICH *Miniport_,
IN PPORTWAVEPCISTREAM PortStream_,
IN ULONG Channel_,
IN BOOLEAN Capture_,
IN PKSDATAFORMAT DataFormat_,
OUT PSERVICEGROUP *ServiceGroup_
)
{
PAGED_CODE ();
DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::Init]"));
ASSERT (Miniport_);
ASSERT (PortStream_);
ASSERT (DataFormat_);
ASSERT (ServiceGroup_);
//
// The rule here is that we return when we fail without a cleanup.
// The destructor will relase the allocated memory.
//
NTSTATUS ntStatus = STATUS_SUCCESS;
//
// Initialize BDL info.
//
stBDList.pBDEntry = NULL;
stBDList.pMapData = NULL;
stBDList.nHead = 0;
stBDList.nTail = 0;
stBDList.ulTagCounter = 0;
stBDList.nBDEntries = 0;
//
// Save miniport pointer and addref it.
//
Wave = Miniport_;
Wave->AddRef ();
//
// Save portstream interface pointer and addref it.
//
PortStream = PortStream_;
PortStream->AddRef ();
//
// Save channel ID and capture flag.
//
Channel = Channel_;
Capture = Capture_;
//
// Save data format and current sample rate.
//
DataFormat = (PKSDATAFORMAT_WAVEFORMATEX)DataFormat_;
CurrentRate = DataFormat->WaveFormatEx.nSamplesPerSec;
NumberOfChannels = DataFormat->WaveFormatEx.nChannels;
//
// Initialize the BDL spinlock.
//
KeInitializeSpinLock (&MapLock);
//
// Create a service group (a DPC abstraction/helper) to help with
// interrupts.
//
ntStatus = PcNewServiceGroup (&ServiceGroup, NULL);
if (!NT_SUCCESS (ntStatus))
{
DOUT (DBG_ERROR, ("Failed to create a service group!"));
return ntStatus;
}
//
// Pass the ServiceGroup pointer to portcls.
//
*ServiceGroup_ = ServiceGroup;
ServiceGroup->AddRef ();
//
// Setup the Buffer Descriptor List (BDL)
// Allocate 32 entries of 8 bytes (one BDL entry). We allocate two tables
// because we need one table as a backup.
// The pointer is aligned on a 8 byte boundary (that's what we need).
//
stBDList.pBDEntry = (tBDEntry *)HalAllocateCommonBuffer (Wave->AdapterObject,
MAX_BDL_ENTRIES * sizeof (tBDEntry) * 2,
&stBDList.PhysAddr,
FALSE);
if (!stBDList.pBDEntry)
{
DOUT (DBG_ERROR, ("Failed HalAllocateCommonBuffer!"));
return STATUS_INSUFFICIENT_RESOURCES;
}
// calculate the (backup) pointer.
stBDList.pBDEntryBackup = (tBDEntry *)stBDList.pBDEntry + MAX_BDL_ENTRIES;
//
// Allocate a buffer for the 32 possible mappings. We allocate two tables
// because we need one table as a backup
//
stBDList.pMapData =
(tMapData *)ExAllocatePool (NonPagedPool, sizeof(tMapData) *
MAX_BDL_ENTRIES * 2);
if (!stBDList.pMapData)
{
DOUT (DBG_ERROR, ("Failed to allocate the back up buffer!"));
return STATUS_INSUFFICIENT_RESOURCES;
}
// calculate the (backup) pointer.
stBDList.pMapDataBackup = stBDList.pMapData + MAX_BDL_ENTRIES;
//
// Store the base address of this DMA engine.
//
if (Capture)
{
//
// could be PCM or MIC capture
//
if (Channel == PIN_WAVEIN_OFFSET)
{
// Base address for DMA registers.
m_ulBDAddr = PI_BDBAR;
}
else
{
// Base address for DMA registers.
m_ulBDAddr = MC_BDBAR;
}
}
else // render
{
// Base address for DMA registers.
m_ulBDAddr = PO_BDBAR;
}
//
// Reset the DMA and set the BD list pointer.
//
ResetDMA ();
//
// Reset the position pointers.
//
TotalBytesMapped = 0;
TotalBytesReleased = 0;
//
// Now set the requested sample rate. In case of a failure, the object
// gets destroyed and releases all memory etc.
//
ntStatus = SetFormat (DataFormat_);
if (!NT_SUCCESS (ntStatus))
{
DOUT (DBG_ERROR, ("Stream init SetFormat call failed!"));
return ntStatus;
}
//
// Initialize the device state.
//
m_PowerState = PowerDeviceD0;
PPREFETCHOFFSET PreFetchOffset;
//
// Query for the new interface "PreFetchOffset" and use
// function offered there in case the interface is offered.
//
if (NT_SUCCESS(PortStream->QueryInterface(IID_IPreFetchOffset, (PVOID *)&PreFetchOffset)))
{
// why don't we pad by 32 sample frames
PreFetchOffset->SetPreFetchOffset(32 * (DataFormat->WaveFormatEx.nChannels * 2));
PreFetchOffset->Release();
}
//
// Store the stream pointer, it is used by the ISR.
//
Wave->Streams[Channel] = this;
return STATUS_SUCCESS;
}
/*****************************************************************************
* CMiniportWaveICHStream::NonDelegatingQueryInterface
*****************************************************************************
* Obtains an interface. This function works just like a COM QueryInterface
* call and is used if the object is not being aggregated.
*/
STDMETHODIMP_(NTSTATUS) CMiniportWaveICHStream::NonDelegatingQueryInterface
(
IN REFIID Interface,
OUT PVOID * Object
)
{
PAGED_CODE ();
ASSERT (Object);
DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::NonDelegatingQueryInterface]"));
//
// Convert for IID_IMiniportWavePciStream
//
if (IsEqualGUIDAligned (Interface, IID_IMiniportWavePciStream))
{
*Object = (PVOID)(PMINIPORTWAVEPCISTREAM)this;
}
//
// Convert for IID_IServiceSink
//
else if (IsEqualGUIDAligned (Interface, IID_IServiceSink))
{
*Object = (PVOID)(PSERVICESINK)this;
}
//
// Convert for IID_IDrmAudioStream
//
else if (IsEqualGUIDAligned (Interface, IID_IDrmAudioStream))
{
*Object = (PVOID)(PDRMAUDIOSTREAM)this;
}
//
// Convert for IID_IUnknown
//
else if (IsEqualGUIDAligned (Interface, IID_IUnknown))
{
*Object = (PVOID)(PUNKNOWN)(PMINIPORTWAVEPCISTREAM)this;
}
else
{
*Object = NULL;
return STATUS_INVALID_PARAMETER;
}
((PUNKNOWN)*Object)->AddRef ();
return STATUS_SUCCESS;
}
/*****************************************************************************
* CMiniportWaveICHStream::GetAllocatorFraming
*****************************************************************************
* Returns the framing requirements for this device.
* That is sample size (for one sample) and preferred frame (buffer) size.
*/
STDMETHODIMP_(NTSTATUS) CMiniportWaveICHStream::GetAllocatorFraming
(
OUT PKSALLOCATOR_FRAMING AllocatorFraming
)
{
PAGED_CODE ();
ULONG SampleSize;
DOUT (DBG_PRINT, ("[CMiniportWaveICHStream::GetAllocatorFraming]"));
//
// Determine sample size in bytes. Always number of
// channels * 2 (because 16-bit).
//
SampleSize = DataFormat->WaveFormatEx.nChannels * 2;
//
// Report the suggested requirements.
//
AllocatorFraming->RequirementsFlags =
KSALLOCATOR_REQUIREMENTF_SYSTEM_MEMORY |
KSALLOCATOR_REQUIREMENTF_PREFERENCES_ONLY;
AllocatorFraming->Frames = 8;
//
// Currently, arbitrarily selecting 10ms as the frame target size.
//
// This value needs to be sample block aligned for ICH to work correctly.
// Assumes 100Hz minimum sample rate (otherwise FrameSize is 0 bytes)
//
AllocatorFraming->FrameSize = SampleSize * (DataFormat->WaveFormatEx.nSamplesPerSec / 100);
AllocatorFraming->FileAlignment = FILE_LONG_ALIGNMENT;
AllocatorFraming->PoolType = NonPagedPool;
return STATUS_SUCCESS;
}
/*****************************************************************************
* CMiniportWaveICHStream::SetFormat
*****************************************************************************
* This routine tests for proper data format (calls wave miniport) and sets
* or changes the stream data format.
* To figure out if the codec supports the sample rate, we just program the
* sample rate and read it back. If it matches we return happy, if not then
* we restore the sample rate and return unhappy.
* We fail this routine if we are currently running (playing or recording).
*/
STDMETHODIMP_(NTSTATUS) CMiniportWaveICHStream::SetFormat
(
IN PKSDATAFORMAT Format
)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -