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.
1878 lines
59 KiB
1878 lines
59 KiB
/*****************************************************************************
|
|
* porthelp.cpp - WDM Streaming port class driver port helper functions
|
|
*****************************************************************************
|
|
* Copyright (c) 1996-2000 Microsoft Corporation. All rights reserved.
|
|
*/
|
|
|
|
#include "private.h"
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* Functions.
|
|
*/
|
|
|
|
#pragma code_seg("PAGE")
|
|
|
|
static
|
|
KSPIN_MEDIUM PinMediums[] =
|
|
{
|
|
{
|
|
STATICGUIDOF(KSMEDIUMSETID_Standard),
|
|
KSMEDIUM_STANDARD_DEVIO,
|
|
0
|
|
}
|
|
};
|
|
|
|
|
|
#define UPTOQUAD(x) (((x)+7)&~7)
|
|
|
|
/*****************************************************************************
|
|
* PrivateHeap
|
|
*****************************************************************************
|
|
* Class for managing a private heap.
|
|
*/
|
|
class PrivateHeap
|
|
{
|
|
private:
|
|
PBYTE m_pbTop;
|
|
PBYTE m_pbCurrent;
|
|
ULONG m_ulSize;
|
|
|
|
public:
|
|
PrivateHeap(void) : m_pbTop(NULL),
|
|
m_pbCurrent(NULL),
|
|
m_ulSize(NULL)
|
|
{
|
|
}
|
|
|
|
//
|
|
// Increase the number of bytes that will be allocated for the heap.
|
|
//
|
|
ULONG Reserve(ULONG ulBytes)
|
|
{
|
|
ASSERT(! m_pbTop);
|
|
ASSERT(! m_pbCurrent);
|
|
|
|
m_ulSize += UPTOQUAD(ulBytes);
|
|
|
|
return m_ulSize;
|
|
}
|
|
|
|
//
|
|
// Allocate memory for the private heap from a pool.
|
|
//
|
|
NTSTATUS AllocateFromPool(POOL_TYPE poolType,ULONG ulTag)
|
|
{
|
|
ASSERT(! m_pbTop);
|
|
ASSERT(! m_pbCurrent);
|
|
ASSERT(m_ulSize);
|
|
|
|
m_pbTop = new(poolType,ulTag) BYTE[m_ulSize];
|
|
|
|
if (! m_pbTop)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
m_pbCurrent = m_pbTop;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Allocate memory from the heap.
|
|
//
|
|
PVOID Alloc(ULONG ulSize)
|
|
{
|
|
ASSERT(ulSize);
|
|
ASSERT(m_pbTop);
|
|
ASSERT(m_pbCurrent);
|
|
ASSERT(m_pbCurrent + UPTOQUAD(ulSize) <= m_pbTop + m_ulSize);
|
|
|
|
PVOID pvResult = PVOID(m_pbCurrent);
|
|
|
|
m_pbCurrent += UPTOQUAD(ulSize);
|
|
|
|
return pvResult;
|
|
}
|
|
|
|
//
|
|
// Determine the amount of space remaining in the heap.
|
|
//
|
|
ULONG BytesRemaining(void)
|
|
{
|
|
ASSERT(m_pbTop);
|
|
ASSERT(m_pbCurrent);
|
|
ASSERT(m_pbCurrent <= m_pbTop + m_ulSize);
|
|
|
|
return ULONG((m_pbTop + m_ulSize) - m_pbCurrent);
|
|
}
|
|
};
|
|
|
|
/*****************************************************************************
|
|
* ::new()
|
|
*****************************************************************************
|
|
* New function for creating objects with private heap.
|
|
*/
|
|
inline PVOID operator new
|
|
(
|
|
size_t iSize,
|
|
PrivateHeap& privateHeap
|
|
)
|
|
{
|
|
return privateHeap.Alloc(ULONG(iSize));
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* MeasureDataRanges()
|
|
*****************************************************************************
|
|
* Determine how much a set of data ranges will expand as a result
|
|
* of cloning WAVEFORMATEX ranges into identical DSOUND ranges.
|
|
*
|
|
* As of WinME, we also clone non-PCM ranges.
|
|
*/
|
|
static
|
|
ULONG
|
|
MeasureDataRanges
|
|
(
|
|
IN PrivateHeap * pPrivateHeap OPTIONAL,
|
|
IN ULONG ulDataRangeCountIn,
|
|
IN KSDATARANGE *const * ppKsDataRangeIn
|
|
)
|
|
{
|
|
ULONG ulNewDataRangeCount = ulDataRangeCountIn;
|
|
|
|
for (ULONG ul = ulDataRangeCountIn; ul--; )
|
|
{
|
|
ASSERT(ppKsDataRangeIn);
|
|
|
|
if ( ( (*ppKsDataRangeIn)->FormatSize
|
|
>= sizeof(KSDATAFORMAT_WAVEFORMATEX)
|
|
)
|
|
&& IsEqualGUIDAligned
|
|
( (*ppKsDataRangeIn)->MajorFormat,
|
|
KSDATAFORMAT_TYPE_AUDIO
|
|
)
|
|
&& IsEqualGUIDAligned
|
|
( (*ppKsDataRangeIn)->Specifier,
|
|
KSDATAFORMAT_SPECIFIER_WAVEFORMATEX
|
|
)
|
|
)
|
|
{
|
|
ulNewDataRangeCount++;
|
|
if (pPrivateHeap)
|
|
{
|
|
pPrivateHeap->Reserve((*ppKsDataRangeIn)->FormatSize);
|
|
}
|
|
}
|
|
|
|
ppKsDataRangeIn++;
|
|
}
|
|
|
|
if (pPrivateHeap && (ulNewDataRangeCount != ulDataRangeCountIn))
|
|
{
|
|
pPrivateHeap->Reserve(ulNewDataRangeCount * sizeof(PKSDATARANGE));
|
|
}
|
|
|
|
return ulNewDataRangeCount;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CloneDataRanges()
|
|
*****************************************************************************
|
|
* Expand data ranges to include DSound formats.
|
|
*/
|
|
static
|
|
const PKSDATARANGE *
|
|
CloneDataRanges
|
|
(
|
|
IN PrivateHeap& privateHeap,
|
|
OUT PULONG pulDataRangeCountOut,
|
|
IN ULONG ulDataRangeCountIn,
|
|
IN KSDATARANGE *const * ppKsDataRangeIn
|
|
)
|
|
{
|
|
ASSERT(pulDataRangeCountOut);
|
|
|
|
//
|
|
// Determine how many data ranges there will be and how much space will be
|
|
// required for the new ones.
|
|
//
|
|
ULONG ulDataRangeCountOut =
|
|
MeasureDataRanges(NULL,ulDataRangeCountIn,ppKsDataRangeIn);
|
|
|
|
const PKSDATARANGE *ppKsDataRangeOut;
|
|
|
|
if (ulDataRangeCountOut == ulDataRangeCountIn)
|
|
{
|
|
//
|
|
// No new data ranges. Use the array we were given.
|
|
//
|
|
ppKsDataRangeOut = ppKsDataRangeIn;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Allocate some space for the new array.
|
|
//
|
|
ppKsDataRangeOut = new(privateHeap) PKSDATARANGE[ulDataRangeCountOut];
|
|
|
|
//
|
|
// Build the new array.
|
|
//
|
|
PKSDATARANGE *ppKsDataRange = (PKSDATARANGE *) ppKsDataRangeOut;
|
|
while (ulDataRangeCountIn--)
|
|
{
|
|
ASSERT(ppKsDataRangeIn);
|
|
|
|
//
|
|
// All the data ranges get copied.
|
|
//
|
|
*ppKsDataRange++ = *ppKsDataRangeIn;
|
|
|
|
//
|
|
// Check for WaveFormatEx datarange
|
|
// This includes non-PCM subformats....
|
|
//
|
|
if ( ( (*ppKsDataRangeIn)->FormatSize
|
|
>= sizeof(KSDATAFORMAT_WAVEFORMATEX)
|
|
)
|
|
&& IsEqualGUIDAligned
|
|
( (*ppKsDataRangeIn)->MajorFormat,
|
|
KSDATAFORMAT_TYPE_AUDIO
|
|
)
|
|
&& IsEqualGUIDAligned
|
|
( (*ppKsDataRangeIn)->Specifier,
|
|
KSDATAFORMAT_SPECIFIER_WAVEFORMATEX
|
|
)
|
|
)
|
|
{
|
|
//
|
|
// WaveFormatEx datarange will require DSound clone.
|
|
// Allocate memory for it and copy.
|
|
//
|
|
*ppKsDataRange =
|
|
PKSDATARANGE
|
|
(
|
|
new(privateHeap) BYTE[(*ppKsDataRangeIn)->FormatSize]
|
|
);
|
|
|
|
RtlCopyMemory
|
|
(
|
|
*ppKsDataRange,
|
|
*ppKsDataRangeIn,
|
|
(*ppKsDataRangeIn)->FormatSize
|
|
);
|
|
|
|
//
|
|
// Update the specifier.
|
|
//
|
|
(*ppKsDataRange++)->Specifier =
|
|
KSDATAFORMAT_SPECIFIER_DSOUND;
|
|
}
|
|
|
|
//
|
|
// Increment input position
|
|
//
|
|
ppKsDataRangeIn++;
|
|
}
|
|
}
|
|
|
|
*pulDataRangeCountOut = ulDataRangeCountOut;
|
|
|
|
return ppKsDataRangeOut;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* PcCreateSubdeviceDescriptor()
|
|
*****************************************************************************
|
|
* Creates a subdevice descriptor.
|
|
*/
|
|
PORTCLASSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
PcCreateSubdeviceDescriptor
|
|
(
|
|
IN PPCFILTER_DESCRIPTOR pPcFilterDescriptor,
|
|
IN ULONG CategoriesCount,
|
|
IN GUID * Categories,
|
|
IN ULONG StreamInterfacesCount,
|
|
IN PKSPIN_INTERFACE StreamInterfaces,
|
|
IN ULONG FilterPropertySetCount,
|
|
IN PKSPROPERTY_SET FilterPropertySets,
|
|
IN ULONG FilterEventSetCount,
|
|
IN PKSEVENT_SET FilterEventSets,
|
|
IN ULONG PinPropertySetCount,
|
|
IN PKSPROPERTY_SET PinPropertySets,
|
|
IN ULONG PinEventSetCount,
|
|
IN PKSEVENT_SET PinEventSets,
|
|
OUT PSUBDEVICE_DESCRIPTOR * OutDescriptor
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(pPcFilterDescriptor);
|
|
ASSERT(OutDescriptor);
|
|
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Calculate how much memory we will need.
|
|
//
|
|
PrivateHeap privateHeap;
|
|
privateHeap.Reserve(sizeof(SUBDEVICE_DESCRIPTOR));
|
|
privateHeap.Reserve(sizeof(KSTOPOLOGY));
|
|
privateHeap.Reserve(sizeof(KSPIN_DESCRIPTOR) * pPcFilterDescriptor->PinCount);
|
|
privateHeap.Reserve(sizeof(PIN_CINSTANCES) * pPcFilterDescriptor->PinCount);
|
|
privateHeap.Reserve(sizeof(PROPERTY_TABLE) * pPcFilterDescriptor->PinCount);
|
|
privateHeap.Reserve(sizeof(EVENT_TABLE) * pPcFilterDescriptor->PinCount);
|
|
|
|
if (pPcFilterDescriptor->NodeCount)
|
|
{
|
|
privateHeap.Reserve(sizeof(GUID) * pPcFilterDescriptor->NodeCount);
|
|
privateHeap.Reserve(sizeof(GUID) * pPcFilterDescriptor->NodeCount);
|
|
}
|
|
|
|
const PCPIN_DESCRIPTOR *pPcPinDescriptor = pPcFilterDescriptor->Pins;
|
|
for (ULONG ul = pPcFilterDescriptor->PinCount; ul--; )
|
|
{
|
|
if (pPcPinDescriptor->KsPinDescriptor.DataRanges)
|
|
{
|
|
MeasureDataRanges( &privateHeap,
|
|
pPcPinDescriptor->KsPinDescriptor.DataRangesCount,
|
|
pPcPinDescriptor->KsPinDescriptor.DataRanges );
|
|
pPcPinDescriptor = PPCPIN_DESCRIPTOR( PBYTE(pPcPinDescriptor) + pPcFilterDescriptor->PinSize );
|
|
}
|
|
else
|
|
{
|
|
ntStatus = STATUS_RANGE_NOT_FOUND; // DataRanges field is NULL
|
|
break; // Don't even bother, just exit
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(ntStatus)) // if fail above, fall through the rest
|
|
{
|
|
//
|
|
// Allocate the required memory.
|
|
//
|
|
ntStatus = privateHeap.AllocateFromPool(PagedPool,'pFcP');
|
|
}
|
|
|
|
if (NT_SUCCESS(ntStatus))
|
|
{
|
|
PSUBDEVICE_DESCRIPTOR descr = new(privateHeap) SUBDEVICE_DESCRIPTOR;
|
|
|
|
//
|
|
// Set up pointers into one big chunk of memory.
|
|
//
|
|
descr->PinCount = pPcFilterDescriptor->PinCount;
|
|
|
|
descr->Topology = new(privateHeap) KSTOPOLOGY;
|
|
descr->PinDescriptors = new(privateHeap) KSPIN_DESCRIPTOR[descr->PinCount];
|
|
descr->PinInstances = new(privateHeap) PIN_CINSTANCES[descr->PinCount];
|
|
descr->PinPropertyTables = new(privateHeap) PROPERTY_TABLE[descr->PinCount];
|
|
descr->PinEventTables = new(privateHeap) EVENT_TABLE[descr->PinCount];
|
|
|
|
if (pPcFilterDescriptor->NodeCount)
|
|
{
|
|
descr->Topology->TopologyNodes =
|
|
new(privateHeap) GUID[pPcFilterDescriptor->NodeCount];
|
|
descr->Topology->TopologyNodesNames =
|
|
new(privateHeap) GUID[pPcFilterDescriptor->NodeCount];
|
|
}
|
|
else
|
|
{
|
|
descr->Topology->TopologyNodes = NULL;
|
|
descr->Topology->TopologyNodesNames = NULL;
|
|
}
|
|
|
|
//
|
|
// Prefer the categories from the filter descriptor.
|
|
//
|
|
if (pPcFilterDescriptor->CategoryCount != 0)
|
|
{
|
|
descr->Topology->CategoriesCount = pPcFilterDescriptor->CategoryCount;
|
|
descr->Topology->Categories = pPcFilterDescriptor->Categories;
|
|
}
|
|
else
|
|
{
|
|
descr->Topology->CategoriesCount = CategoriesCount;
|
|
descr->Topology->Categories = Categories;
|
|
}
|
|
|
|
descr->Topology->TopologyNodesCount = pPcFilterDescriptor->NodeCount;
|
|
descr->Topology->TopologyConnectionsCount = pPcFilterDescriptor->ConnectionCount;
|
|
descr->Topology->TopologyConnections = pPcFilterDescriptor->Connections;
|
|
|
|
//
|
|
// Initialize filter properties.
|
|
//
|
|
descr->FilterPropertyTable.PropertySetCount = FilterPropertySetCount;
|
|
descr->FilterPropertyTable.PropertySets = FilterPropertySets;
|
|
descr->FilterPropertyTable.StaticSets = TRUE;
|
|
|
|
//
|
|
// Initialize filter events.
|
|
//
|
|
descr->FilterEventTable.EventSetCount = FilterEventSetCount;
|
|
descr->FilterEventTable.EventSets = FilterEventSets;
|
|
descr->FilterEventTable.StaticSets = TRUE;
|
|
|
|
//
|
|
// Copy node type and name and merge node properties and events.
|
|
//
|
|
const PCNODE_DESCRIPTOR *pPcNodeDescriptor = pPcFilterDescriptor->Nodes;
|
|
GUID *pGuidType = (GUID *) descr->Topology->TopologyNodes;
|
|
GUID *pGuidName = (GUID *) descr->Topology->TopologyNodesNames;
|
|
for (ULONG node = pPcFilterDescriptor->NodeCount; node--; )
|
|
{
|
|
*pGuidType++ = *pPcNodeDescriptor->Type;
|
|
if (pPcNodeDescriptor->Name)
|
|
{
|
|
*pGuidName++ = *pPcNodeDescriptor->Name;
|
|
}
|
|
else
|
|
{
|
|
*pGuidName++ = *pPcNodeDescriptor->Type;
|
|
}
|
|
|
|
if ( (pPcNodeDescriptor->AutomationTable)
|
|
&& (pPcNodeDescriptor->AutomationTable->PropertyCount)
|
|
)
|
|
{
|
|
PcAddToPropertyTable
|
|
(
|
|
&descr->FilterPropertyTable,
|
|
pPcNodeDescriptor->AutomationTable->PropertyCount,
|
|
pPcNodeDescriptor->AutomationTable->Properties,
|
|
pPcNodeDescriptor->AutomationTable->PropertyItemSize,
|
|
TRUE
|
|
);
|
|
}
|
|
|
|
if ( (pPcNodeDescriptor->AutomationTable)
|
|
&& (pPcNodeDescriptor->AutomationTable->EventCount)
|
|
)
|
|
{
|
|
PcAddToEventTable
|
|
(
|
|
&descr->FilterEventTable,
|
|
pPcNodeDescriptor->AutomationTable->EventCount,
|
|
pPcNodeDescriptor->AutomationTable->Events,
|
|
pPcNodeDescriptor->AutomationTable->EventItemSize,
|
|
TRUE
|
|
);
|
|
}
|
|
|
|
pPcNodeDescriptor =
|
|
PPCNODE_DESCRIPTOR
|
|
( PBYTE(pPcNodeDescriptor) + pPcFilterDescriptor->NodeSize
|
|
);
|
|
}
|
|
|
|
//
|
|
// Merge filter properties.
|
|
//
|
|
if ( (pPcFilterDescriptor->AutomationTable)
|
|
&& (pPcFilterDescriptor->AutomationTable->PropertyCount)
|
|
)
|
|
{
|
|
PcAddToPropertyTable
|
|
(
|
|
&descr->FilterPropertyTable,
|
|
pPcFilterDescriptor->AutomationTable->PropertyCount,
|
|
pPcFilterDescriptor->AutomationTable->Properties,
|
|
pPcFilterDescriptor->AutomationTable->PropertyItemSize,
|
|
FALSE
|
|
);
|
|
}
|
|
|
|
//
|
|
// Merge filter events.
|
|
//
|
|
if ( (pPcFilterDescriptor->AutomationTable)
|
|
&& (pPcFilterDescriptor->AutomationTable->EventCount)
|
|
)
|
|
{
|
|
PcAddToEventTable
|
|
(
|
|
&descr->FilterEventTable,
|
|
pPcFilterDescriptor->AutomationTable->EventCount,
|
|
pPcFilterDescriptor->AutomationTable->Events,
|
|
pPcFilterDescriptor->AutomationTable->EventItemSize,
|
|
FALSE
|
|
);
|
|
}
|
|
|
|
//
|
|
// Do per-pin stuff.
|
|
//
|
|
PPROPERTY_TABLE pt = descr->PinPropertyTables;
|
|
PEVENT_TABLE et = descr->PinEventTables;
|
|
PKSPIN_DESCRIPTOR p = descr->PinDescriptors;
|
|
PPIN_CINSTANCES i = descr->PinInstances;
|
|
|
|
pPcPinDescriptor = PPCPIN_DESCRIPTOR(pPcFilterDescriptor->Pins);
|
|
for
|
|
(
|
|
ULONG pin = 0;
|
|
pin < pPcFilterDescriptor->PinCount;
|
|
pin++
|
|
)
|
|
{
|
|
//
|
|
// Find a pin that has the same property set.
|
|
//
|
|
PPROPERTY_TABLE twinPt = descr->PinPropertyTables;
|
|
PPCPIN_DESCRIPTOR pPcPinDescriptorTwin =
|
|
PPCPIN_DESCRIPTOR(pPcFilterDescriptor->Pins);
|
|
for
|
|
(
|
|
ULONG twinPin = 0;
|
|
twinPin < pin;
|
|
twinPin++, twinPt++
|
|
)
|
|
{
|
|
if ( ( pPcPinDescriptor->AutomationTable
|
|
== pPcPinDescriptorTwin->AutomationTable
|
|
)
|
|
|| ( pPcPinDescriptor->AutomationTable
|
|
&& pPcPinDescriptorTwin->AutomationTable
|
|
&& ( pPcPinDescriptor->AutomationTable->PropertyCount
|
|
== pPcPinDescriptorTwin->AutomationTable->PropertyCount
|
|
)
|
|
&& ( pPcPinDescriptor->AutomationTable->Properties
|
|
== pPcPinDescriptorTwin->AutomationTable->Properties
|
|
)
|
|
&& ( pPcPinDescriptor->AutomationTable->PropertyItemSize
|
|
== pPcPinDescriptorTwin->AutomationTable->PropertyItemSize
|
|
)
|
|
)
|
|
)
|
|
{
|
|
*pt = *twinPt;
|
|
break;
|
|
}
|
|
|
|
pPcPinDescriptorTwin =
|
|
PPCPIN_DESCRIPTOR
|
|
( PBYTE(pPcPinDescriptorTwin) + pPcFilterDescriptor->PinSize
|
|
);
|
|
}
|
|
|
|
//
|
|
// Create a new table if we have to.
|
|
//
|
|
if (twinPin == pin)
|
|
{
|
|
pt->PropertySetCount = PinPropertySetCount;
|
|
pt->PropertySets = PinPropertySets;
|
|
pt->StaticSets = TRUE;
|
|
|
|
if ( (pPcPinDescriptor->AutomationTable)
|
|
&& (pPcPinDescriptor->AutomationTable->PropertyCount)
|
|
)
|
|
{
|
|
PcAddToPropertyTable
|
|
(
|
|
pt,
|
|
pPcPinDescriptor->AutomationTable->PropertyCount,
|
|
pPcPinDescriptor->AutomationTable->Properties,
|
|
pPcPinDescriptor->AutomationTable->PropertyItemSize,
|
|
FALSE
|
|
);
|
|
}
|
|
|
|
const PCNODE_DESCRIPTOR *pPcNodeDescriptor2 = pPcFilterDescriptor->Nodes;
|
|
for (ULONG node = pPcFilterDescriptor->NodeCount; node--; )
|
|
{
|
|
if ( (pPcNodeDescriptor2->AutomationTable)
|
|
&& (pPcNodeDescriptor2->AutomationTable->PropertyCount)
|
|
)
|
|
{
|
|
PcAddToPropertyTable
|
|
(
|
|
pt,
|
|
pPcNodeDescriptor2->AutomationTable->PropertyCount,
|
|
pPcNodeDescriptor2->AutomationTable->Properties,
|
|
pPcNodeDescriptor2->AutomationTable->PropertyItemSize,
|
|
TRUE
|
|
);
|
|
}
|
|
|
|
pPcNodeDescriptor2 =
|
|
PPCNODE_DESCRIPTOR
|
|
( PBYTE(pPcNodeDescriptor2) + pPcFilterDescriptor->NodeSize
|
|
);
|
|
}
|
|
}
|
|
pt++;
|
|
|
|
//
|
|
// Find a pin that has the same event set.
|
|
//
|
|
PEVENT_TABLE twinEt = descr->PinEventTables;
|
|
pPcPinDescriptorTwin = PPCPIN_DESCRIPTOR(pPcFilterDescriptor->Pins);
|
|
for
|
|
(
|
|
ULONG twinEPin = 0;
|
|
twinEPin < pin;
|
|
twinEPin++, twinEt++
|
|
)
|
|
{
|
|
if ( ( pPcPinDescriptor->AutomationTable
|
|
== pPcPinDescriptorTwin->AutomationTable
|
|
)
|
|
|| ( pPcPinDescriptor->AutomationTable
|
|
&& pPcPinDescriptorTwin->AutomationTable
|
|
&& ( pPcPinDescriptor->AutomationTable->EventCount
|
|
== pPcPinDescriptorTwin->AutomationTable->EventCount
|
|
)
|
|
&& ( pPcPinDescriptor->AutomationTable->Events
|
|
== pPcPinDescriptorTwin->AutomationTable->Events
|
|
)
|
|
&& ( pPcPinDescriptor->AutomationTable->EventItemSize
|
|
== pPcPinDescriptorTwin->AutomationTable->EventItemSize
|
|
)
|
|
)
|
|
)
|
|
{
|
|
*et = *twinEt;
|
|
break;
|
|
}
|
|
|
|
pPcPinDescriptorTwin =
|
|
PPCPIN_DESCRIPTOR
|
|
( PBYTE(pPcPinDescriptorTwin) + pPcFilterDescriptor->PinSize
|
|
);
|
|
}
|
|
|
|
//
|
|
// Create a new table if we have to.
|
|
//
|
|
if (twinEPin == pin)
|
|
{
|
|
et->EventSetCount = PinEventSetCount;
|
|
et->EventSets = PinEventSets;
|
|
et->StaticSets = TRUE;
|
|
|
|
if ( (pPcPinDescriptor->AutomationTable)
|
|
&& (pPcPinDescriptor->AutomationTable->EventCount)
|
|
)
|
|
{
|
|
PcAddToEventTable
|
|
(
|
|
et,
|
|
pPcPinDescriptor->AutomationTable->EventCount,
|
|
pPcPinDescriptor->AutomationTable->Events,
|
|
pPcPinDescriptor->AutomationTable->EventItemSize,
|
|
FALSE
|
|
);
|
|
}
|
|
|
|
const PCNODE_DESCRIPTOR *pPcNodeDescriptor2 = pPcFilterDescriptor->Nodes;
|
|
for( ULONG node = pPcFilterDescriptor->NodeCount; node--; )
|
|
{
|
|
if ( (pPcNodeDescriptor2->AutomationTable)
|
|
&& (pPcNodeDescriptor2->AutomationTable->EventCount)
|
|
)
|
|
{
|
|
PcAddToEventTable
|
|
(
|
|
et,
|
|
pPcNodeDescriptor2->AutomationTable->EventCount,
|
|
pPcNodeDescriptor2->AutomationTable->Events,
|
|
pPcNodeDescriptor2->AutomationTable->EventItemSize,
|
|
TRUE
|
|
);
|
|
}
|
|
|
|
pPcNodeDescriptor2 = PPCNODE_DESCRIPTOR( PBYTE(pPcNodeDescriptor2) + pPcFilterDescriptor->NodeSize );
|
|
}
|
|
}
|
|
et++;
|
|
|
|
//
|
|
// Copy the KS descriptor.
|
|
//
|
|
*p = pPcPinDescriptor->KsPinDescriptor;
|
|
|
|
//
|
|
// Provide default mediums if necessary.
|
|
//
|
|
if (p->Mediums == NULL)
|
|
{
|
|
p->MediumsCount = SIZEOF_ARRAY(PinMediums);
|
|
p->Mediums = PinMediums;
|
|
}
|
|
|
|
//
|
|
// Massage the data ranges.
|
|
//
|
|
p->DataRanges =
|
|
CloneDataRanges
|
|
(
|
|
privateHeap,
|
|
&p->DataRangesCount,
|
|
pPcPinDescriptor->KsPinDescriptor.DataRangesCount,
|
|
pPcPinDescriptor->KsPinDescriptor.DataRanges
|
|
);
|
|
|
|
//
|
|
// Provide default interfaces if necessary.
|
|
//
|
|
if ( (p->Communication & KSPIN_COMMUNICATION_BOTH)
|
|
&& (p->Interfaces == NULL)
|
|
)
|
|
{
|
|
p->InterfacesCount = StreamInterfacesCount;
|
|
p->Interfaces = StreamInterfaces;
|
|
}
|
|
p++;
|
|
|
|
i->FilterPossible = pPcPinDescriptor->MaxFilterInstanceCount;
|
|
i->FilterNecessary = pPcPinDescriptor->MinFilterInstanceCount;
|
|
i->GlobalPossible = pPcPinDescriptor->MaxGlobalInstanceCount;
|
|
i->GlobalCurrent = 0;
|
|
i++;
|
|
|
|
pPcPinDescriptor =
|
|
PPCPIN_DESCRIPTOR
|
|
( PBYTE(pPcPinDescriptor) + pPcFilterDescriptor->PinSize
|
|
);
|
|
}
|
|
|
|
*OutDescriptor = descr;
|
|
|
|
ASSERT(privateHeap.BytesRemaining() == 0);
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* PcDeleteSubdeviceDescriptor()
|
|
*****************************************************************************
|
|
* Deletes a subdevice descriptor.
|
|
*/
|
|
PORTCLASSAPI
|
|
void
|
|
NTAPI
|
|
PcDeleteSubdeviceDescriptor
|
|
(
|
|
IN PSUBDEVICE_DESCRIPTOR pSubdeviceDescriptor
|
|
)
|
|
{
|
|
//
|
|
// Free allocated memory for filter property and event tables.
|
|
//
|
|
PcFreePropertyTable(&pSubdeviceDescriptor->FilterPropertyTable);
|
|
PcFreeEventTable(&pSubdeviceDescriptor->FilterEventTable);
|
|
|
|
//
|
|
// Free allocated memory for pin property tables.
|
|
//
|
|
PPROPERTY_TABLE pPropertyTable = pSubdeviceDescriptor->PinPropertyTables;
|
|
for (ULONG ul = pSubdeviceDescriptor->PinCount; ul--; pPropertyTable++)
|
|
{
|
|
//
|
|
// Find and clear any references to the same property set.
|
|
//
|
|
for
|
|
( PPROPERTY_TABLE pPropertyTableTwin =
|
|
( pSubdeviceDescriptor->PinPropertyTables
|
|
+ ( pSubdeviceDescriptor->PinCount
|
|
- 1
|
|
)
|
|
)
|
|
; pPropertyTableTwin != pPropertyTable
|
|
; pPropertyTableTwin--
|
|
)
|
|
{
|
|
if
|
|
( pPropertyTableTwin->PropertySets
|
|
== pPropertyTable->PropertySets
|
|
)
|
|
{
|
|
pPropertyTableTwin->PropertySetCount = 0;
|
|
pPropertyTableTwin->PropertySets = NULL;
|
|
pPropertyTableTwin->StaticSets = TRUE;
|
|
pPropertyTableTwin->StaticItems = NULL;
|
|
}
|
|
}
|
|
|
|
PcFreePropertyTable(pPropertyTable);
|
|
}
|
|
|
|
//
|
|
// Free allocated memory for pin event tables.
|
|
//
|
|
PEVENT_TABLE pEventTable = pSubdeviceDescriptor->PinEventTables;
|
|
for (ul = pSubdeviceDescriptor->PinCount; ul--; pEventTable++)
|
|
{
|
|
//
|
|
// Find and clear any references to the same event set.
|
|
//
|
|
for
|
|
( PEVENT_TABLE pEventTableTwin =
|
|
( pSubdeviceDescriptor->PinEventTables
|
|
+ ( pSubdeviceDescriptor->PinCount
|
|
- 1
|
|
)
|
|
)
|
|
; pEventTableTwin != pEventTable
|
|
; pEventTableTwin--
|
|
)
|
|
{
|
|
if
|
|
( pEventTableTwin->EventSets
|
|
== pEventTable->EventSets
|
|
)
|
|
{
|
|
pEventTableTwin->EventSetCount = 0;
|
|
pEventTableTwin->EventSets = NULL;
|
|
pEventTableTwin->StaticSets = TRUE;
|
|
pEventTableTwin->StaticItems = NULL;
|
|
}
|
|
}
|
|
|
|
PcFreeEventTable(pEventTable);
|
|
}
|
|
|
|
//
|
|
// The rest is one big chunk.
|
|
//
|
|
delete [] PBYTE(pSubdeviceDescriptor);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* PcValidateConnectRequest()
|
|
*****************************************************************************
|
|
* Validate attempt to create a pin.
|
|
*/
|
|
PORTCLASSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
PcValidateConnectRequest
|
|
( IN PIRP pIrp
|
|
, IN PSUBDEVICE_DESCRIPTOR pSubdeviceDescriptor
|
|
, OUT PKSPIN_CONNECT * ppKsPinConnect
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(pIrp);
|
|
ASSERT(pSubdeviceDescriptor);
|
|
ASSERT(ppKsPinConnect);
|
|
|
|
NTSTATUS ntStatus =
|
|
KsValidateConnectRequest
|
|
( pIrp
|
|
, pSubdeviceDescriptor->PinCount
|
|
, pSubdeviceDescriptor->PinDescriptors
|
|
, ppKsPinConnect
|
|
);
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* PcValidatePinCount()
|
|
*****************************************************************************
|
|
* Validate pin count.
|
|
*/
|
|
PORTCLASSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
PcValidatePinCount
|
|
( IN ULONG ulPinId
|
|
, IN PSUBDEVICE_DESCRIPTOR pSubdeviceDescriptor
|
|
, IN PULONG pulPinInstanceCounts
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(pSubdeviceDescriptor);
|
|
ASSERT(pulPinInstanceCounts);
|
|
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
|
|
if
|
|
( ( pSubdeviceDescriptor->PinInstances[ulPinId].GlobalCurrent
|
|
< pSubdeviceDescriptor->PinInstances[ulPinId].GlobalPossible
|
|
)
|
|
|| ( pulPinInstanceCounts[ulPinId]
|
|
< pSubdeviceDescriptor->PinInstances[ulPinId].FilterPossible
|
|
)
|
|
)
|
|
{
|
|
pSubdeviceDescriptor->PinInstances[ulPinId].GlobalCurrent++;
|
|
pulPinInstanceCounts[ulPinId]++;
|
|
|
|
_DbgPrintF( DEBUGLVL_VERBOSE,
|
|
( "Create pin %d: global=%d local=%d"
|
|
, ulPinId
|
|
, pSubdeviceDescriptor->PinInstances[ulPinId].GlobalCurrent
|
|
, pulPinInstanceCounts[ulPinId]
|
|
));
|
|
}
|
|
else
|
|
{
|
|
// TODO: What code?
|
|
ntStatus = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* PcValidateDeviceContext()
|
|
*****************************************************************************
|
|
* Probes DeviceContext for writing.
|
|
*/
|
|
PORTCLASSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
PcValidateDeviceContext
|
|
( IN PDEVICE_CONTEXT pDeviceContext,
|
|
IN PIRP pIrp
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
|
|
if (NULL == pDeviceContext)
|
|
{
|
|
_DbgPrintF(DEBUGLVL_TERSE, ("PcValidateDeviceContext : pDeviceContext = NULL"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
// validate the pointers if we don't trust the client
|
|
//
|
|
/*
|
|
// ISSUE ALPERS 2000/12/20 - The Probe call is disabled because it always generates an exception.
|
|
// Therefore it is disabled.
|
|
|
|
if (KernelMode != pIrp->RequestorMode)
|
|
{
|
|
__try
|
|
{
|
|
ProbeForRead( pDeviceContext,
|
|
sizeof(*pDeviceContext),
|
|
sizeof(BYTE));
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
ntStatus = GetExceptionCode();
|
|
_DbgPrintF(DEBUGLVL_TERSE, ("PcValidateDeviceContext : ProbeForWrite failed %X", ntStatus));
|
|
}
|
|
}
|
|
*/
|
|
|
|
if (NT_SUCCESS(ntStatus))
|
|
{
|
|
if (PORTCLS_DEVICE_EXTENSION_SIGNATURE != pDeviceContext->Signature )
|
|
{
|
|
ntStatus = STATUS_INVALID_PARAMETER;
|
|
_DbgPrintF(DEBUGLVL_TERSE, ("PcValidateDeviceContext : Invalid Extension Signature"));
|
|
}
|
|
}
|
|
|
|
return ntStatus;
|
|
} // PcValidateDeviceContext
|
|
|
|
/*****************************************************************************
|
|
* PcTerminateConnection()
|
|
*****************************************************************************
|
|
* Decrement instance counts associated with a pin.
|
|
*/
|
|
PORTCLASSAPI
|
|
void
|
|
NTAPI
|
|
PcTerminateConnection
|
|
( IN PSUBDEVICE_DESCRIPTOR pSubdeviceDescriptor
|
|
, IN PULONG pulPinInstanceCounts
|
|
, IN ULONG ulPinId
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(pSubdeviceDescriptor);
|
|
ASSERT(pulPinInstanceCounts);
|
|
ASSERT(ulPinId <= pSubdeviceDescriptor->PinCount);
|
|
|
|
pSubdeviceDescriptor->PinInstances[ulPinId].GlobalCurrent--;
|
|
pulPinInstanceCounts[ulPinId]--;
|
|
|
|
_DbgPrintF( DEBUGLVL_VERBOSE,
|
|
( "Delete pin %d: global=%d local=%d"
|
|
, ulPinId
|
|
, pSubdeviceDescriptor->PinInstances[ulPinId].GlobalCurrent
|
|
, pulPinInstanceCounts[ulPinId]
|
|
));
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* PcVerifyFilterIsReady()
|
|
*****************************************************************************
|
|
* Verify necessary pins are connected.
|
|
*/
|
|
PORTCLASSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
PcVerifyFilterIsReady
|
|
( IN PSUBDEVICE_DESCRIPTOR pSubdeviceDescriptor
|
|
, IN PULONG pulPinInstanceCounts
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(pSubdeviceDescriptor);
|
|
ASSERT(pulPinInstanceCounts);
|
|
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
for
|
|
( ULONG ulPinId = 0
|
|
; ulPinId < pSubdeviceDescriptor->PinCount
|
|
; ulPinId++
|
|
)
|
|
{
|
|
if
|
|
( pulPinInstanceCounts[ulPinId]
|
|
< pSubdeviceDescriptor->PinInstances[ulPinId].FilterNecessary
|
|
)
|
|
{
|
|
// TODO: What code?
|
|
ntStatus = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
#define END_NONE 0
|
|
#define END_FROM 1
|
|
#define END_TO 2
|
|
#define END_BOTH 3
|
|
|
|
/*****************************************************************************
|
|
* FindConnectionToPin()
|
|
*****************************************************************************
|
|
* Find a connection that connects to a given node or filter pin.
|
|
*
|
|
* ulNode - node number of KSFILTER_NODE
|
|
* ulConnection - in: connection to start with
|
|
* out: found connection or ULONG(-1) if not found
|
|
*
|
|
* return - 0 for no connection found
|
|
* END_FROM for outgoing connection
|
|
* END_TO for incoming connection
|
|
*/
|
|
ULONG
|
|
FindConnectionToPin
|
|
(
|
|
IN ULONG ulNode,
|
|
IN ULONG ulPin,
|
|
IN PKSTOPOLOGY pKsTopology,
|
|
IN OUT PULONG ulConnection,
|
|
OUT PKSTOPOLOGY_CONNECTION * ppKsTopologyConnection OPTIONAL
|
|
)
|
|
{
|
|
ASSERT(pKsTopology);
|
|
ASSERT(ulConnection);
|
|
ASSERT(*ulConnection < pKsTopology->TopologyConnectionsCount);
|
|
|
|
ULONG ulEnd;
|
|
|
|
PKSTOPOLOGY_CONNECTION pKsTopologyConnection =
|
|
PKSTOPOLOGY_CONNECTION
|
|
(
|
|
&pKsTopology->TopologyConnections[*ulConnection]
|
|
);
|
|
|
|
while (1)
|
|
{
|
|
ASSERT(*ulConnection <= pKsTopology->TopologyConnectionsCount);
|
|
|
|
if (*ulConnection == pKsTopology->TopologyConnectionsCount)
|
|
{
|
|
ulEnd = END_NONE;
|
|
*ulConnection = ULONG(-1);
|
|
pKsTopologyConnection = NULL;
|
|
break;
|
|
}
|
|
else
|
|
if ( (pKsTopologyConnection->FromNode == ulNode)
|
|
&& (pKsTopologyConnection->FromNodePin == ulPin)
|
|
)
|
|
{
|
|
ulEnd = END_FROM;
|
|
break;
|
|
}
|
|
else
|
|
if ( (pKsTopologyConnection->ToNode == ulNode)
|
|
&& (pKsTopologyConnection->ToNodePin == ulPin)
|
|
)
|
|
{
|
|
ulEnd = END_TO;
|
|
break;
|
|
}
|
|
|
|
(*ulConnection)++;
|
|
pKsTopologyConnection++;
|
|
}
|
|
|
|
if (ppKsTopologyConnection)
|
|
{
|
|
*ppKsTopologyConnection = pKsTopologyConnection;
|
|
}
|
|
|
|
return ulEnd;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* FindConnectionToNode()
|
|
*****************************************************************************
|
|
* Find a connection that connects to a given node or to the filter.
|
|
*
|
|
* ulNode - node number of KSFILTER_NODE
|
|
* ulEnd - 0 for any direction
|
|
* END_FROM for outgoing connection
|
|
* END_TO for incoming connection
|
|
* ulConnection - in: connection to start with
|
|
* out: found connection or ULONG(-1) if not found
|
|
*
|
|
* return - 0 for no connection found
|
|
* END_FROM for outgoing connection
|
|
* END_TO for incoming connection
|
|
*/
|
|
ULONG
|
|
FindConnectionToNode
|
|
(
|
|
IN ULONG ulNode,
|
|
IN ULONG ulEnd,
|
|
IN PKSTOPOLOGY pKsTopology,
|
|
IN OUT PULONG ulConnection,
|
|
OUT PKSTOPOLOGY_CONNECTION * ppKsTopologyConnection OPTIONAL
|
|
)
|
|
{
|
|
ASSERT(pKsTopology);
|
|
ASSERT
|
|
( (ulNode == KSFILTER_NODE)
|
|
|| (ulNode < pKsTopology->TopologyNodesCount)
|
|
);
|
|
ASSERT(ulConnection);
|
|
ASSERT(*ulConnection < pKsTopology->TopologyConnectionsCount);
|
|
|
|
PKSTOPOLOGY_CONNECTION pKsTopologyConnection =
|
|
PKSTOPOLOGY_CONNECTION
|
|
(
|
|
&pKsTopology->TopologyConnections[*ulConnection]
|
|
);
|
|
|
|
while (1)
|
|
{
|
|
ASSERT(*ulConnection <= pKsTopology->TopologyConnectionsCount);
|
|
|
|
if (*ulConnection == pKsTopology->TopologyConnectionsCount)
|
|
{
|
|
ulEnd = END_NONE;
|
|
*ulConnection = ULONG(-1);
|
|
pKsTopologyConnection = NULL;
|
|
break;
|
|
}
|
|
else
|
|
if ( (pKsTopologyConnection->FromNode == ulNode)
|
|
&& (ulEnd != END_TO)
|
|
)
|
|
{
|
|
ulEnd = END_FROM;
|
|
break;
|
|
}
|
|
else
|
|
if ( (pKsTopologyConnection->ToNode == ulNode)
|
|
&& (ulEnd != END_FROM)
|
|
)
|
|
{
|
|
ulEnd = END_TO;
|
|
break;
|
|
}
|
|
|
|
(*ulConnection)++;
|
|
pKsTopologyConnection++;
|
|
}
|
|
|
|
if (ppKsTopologyConnection)
|
|
{
|
|
*ppKsTopologyConnection = pKsTopologyConnection;
|
|
}
|
|
|
|
return ulEnd;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* NodeIsTransform()
|
|
*****************************************************************************
|
|
* Determines if a node is a transform. KSFILTER_NODE is handled (FALSE).
|
|
*/
|
|
BOOLEAN
|
|
NodeIsTransform
|
|
(
|
|
IN ULONG ulNode,
|
|
IN PKSTOPOLOGY pKsTopology
|
|
)
|
|
{
|
|
ASSERT(pKsTopology);
|
|
ASSERT
|
|
( (ulNode == KSFILTER_NODE)
|
|
|| (ulNode < pKsTopology->TopologyNodesCount)
|
|
);
|
|
|
|
ULONG ulEnd = END_NONE;
|
|
|
|
if (ulNode != KSFILTER_NODE)
|
|
{
|
|
PKSTOPOLOGY_CONNECTION pKsTopologyConnection =
|
|
PKSTOPOLOGY_CONNECTION(pKsTopology->TopologyConnections);
|
|
|
|
for
|
|
(
|
|
ULONG ul = pKsTopology->TopologyConnectionsCount;
|
|
ul--;
|
|
pKsTopologyConnection++
|
|
)
|
|
{
|
|
if (pKsTopologyConnection->FromNode == ulNode)
|
|
{
|
|
ulEnd += END_FROM;
|
|
|
|
if ( (ulEnd != END_FROM)
|
|
&& (ulEnd != END_BOTH)
|
|
)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (pKsTopologyConnection->ToNode == ulNode)
|
|
{
|
|
ulEnd += END_TO;
|
|
|
|
if ( (ulEnd != END_TO)
|
|
&& (ulEnd != END_BOTH)
|
|
)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ulEnd == END_BOTH;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* NodeAtThisEnd()
|
|
*****************************************************************************
|
|
* Node at indicated end of the connection.
|
|
*/
|
|
inline
|
|
ULONG
|
|
NodeAtThisEnd
|
|
(
|
|
IN ULONG ulEnd,
|
|
IN PKSTOPOLOGY_CONNECTION pKsTopologyConnection
|
|
)
|
|
{
|
|
return
|
|
( (ulEnd == END_FROM)
|
|
? pKsTopologyConnection->FromNode
|
|
: pKsTopologyConnection->ToNode
|
|
);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* NodeAtOtherEnd()
|
|
*****************************************************************************
|
|
* Node at opposite end of the connection.
|
|
*/
|
|
inline
|
|
ULONG
|
|
NodeAtOtherEnd
|
|
(
|
|
IN ULONG ulEnd,
|
|
IN PKSTOPOLOGY_CONNECTION pKsTopologyConnection
|
|
)
|
|
{
|
|
return
|
|
( (ulEnd == END_FROM)
|
|
? pKsTopologyConnection->ToNode
|
|
: pKsTopologyConnection->FromNode
|
|
);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* PcCaptureFormat()
|
|
*****************************************************************************
|
|
* Capture a data format in an allocated buffer, possibly changing offensive
|
|
* formats.
|
|
*/
|
|
PORTCLASSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
PcCaptureFormat
|
|
(
|
|
OUT PKSDATAFORMAT * ppKsDataFormatOut,
|
|
IN PKSDATAFORMAT pKsDataFormatIn,
|
|
IN PSUBDEVICE_DESCRIPTOR pSubdeviceDescriptor,
|
|
IN ULONG ulPinId
|
|
)
|
|
{
|
|
ASSERT(ppKsDataFormatOut);
|
|
ASSERT(pKsDataFormatIn);
|
|
ASSERT(pSubdeviceDescriptor);
|
|
ASSERT(ulPinId < pSubdeviceDescriptor->PinCount);
|
|
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
|
|
if( (pKsDataFormatIn->FormatSize >= sizeof(KSDATAFORMAT_DSOUND)) &&
|
|
IsEqualGUIDAligned( pKsDataFormatIn->MajorFormat, KSDATAFORMAT_TYPE_AUDIO ) &&
|
|
IsEqualGUIDAligned( pKsDataFormatIn->Specifier, KSDATAFORMAT_SPECIFIER_DSOUND ) )
|
|
{
|
|
//
|
|
// This is the dread DSound format. Check to see if we have the
|
|
// required topology and convert to WaveFormatEx if we do.
|
|
//
|
|
// Note: DSound format does not have to be PCM....
|
|
//
|
|
PKSDATAFORMAT_DSOUND pKsDataFormatDSound =
|
|
PKSDATAFORMAT_DSOUND(pKsDataFormatIn);
|
|
|
|
//
|
|
// Fail if the client has asked for a software buffer.
|
|
//
|
|
if ( pKsDataFormatDSound->BufferDesc.Flags
|
|
& KSDSOUND_BUFFER_LOCSOFTWARE
|
|
)
|
|
{
|
|
_DbgPrintF(DEBUGLVL_TERSE,("PcCaptureFormat Failed because client requested software buffer."));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Find a connection involving the filter pin.
|
|
//
|
|
ULONG ulConnection = 0;
|
|
PKSTOPOLOGY_CONNECTION pKsTopologyConnection;
|
|
ULONG ulEnd =
|
|
FindConnectionToPin
|
|
(
|
|
KSFILTER_NODE,
|
|
ulPinId,
|
|
pSubdeviceDescriptor->Topology,
|
|
&ulConnection,
|
|
&pKsTopologyConnection
|
|
);
|
|
|
|
//
|
|
// Trace the topology until we find a non-transform or all the
|
|
// required nodes have been found. Position notification is
|
|
// always supported.
|
|
//
|
|
ULONG ulMissing =
|
|
( pKsDataFormatDSound->BufferDesc.Control
|
|
& ~KSDSOUND_BUFFER_CTRL_POSITIONNOTIFY
|
|
);
|
|
|
|
while (ulMissing && ulEnd)
|
|
{
|
|
//
|
|
// Found a connection. Follow the topology.
|
|
//
|
|
ULONG ulNode = NodeAtOtherEnd(ulEnd,pKsTopologyConnection);
|
|
|
|
if (! NodeIsTransform(ulNode,pSubdeviceDescriptor->Topology))
|
|
{
|
|
//
|
|
// The new node is not a simple transform (1 in, 1 out).
|
|
//
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Drop 'missing' bits as appropriate based on the node GUID.
|
|
//
|
|
ASSERT(ulNode < pSubdeviceDescriptor->Topology->TopologyNodesCount);
|
|
const GUID *pGuid = &pSubdeviceDescriptor->Topology->TopologyNodes[ulNode];
|
|
if (IsEqualGUIDAligned(*pGuid,KSNODETYPE_3D_EFFECTS))
|
|
{
|
|
ulMissing &=~ KSDSOUND_BUFFER_CTRL_3D;
|
|
}
|
|
else
|
|
if (IsEqualGUIDAligned(*pGuid,KSNODETYPE_SRC))
|
|
{
|
|
ulMissing &=~ KSDSOUND_BUFFER_CTRL_FREQUENCY;
|
|
}
|
|
else
|
|
if ( IsEqualGUIDAligned(*pGuid,KSNODETYPE_SUPERMIX)
|
|
|| IsEqualGUIDAligned(*pGuid,KSNODETYPE_VOLUME)
|
|
)
|
|
{
|
|
ulMissing &=~ KSDSOUND_BUFFER_CTRL_PAN;
|
|
ulMissing &=~ KSDSOUND_BUFFER_CTRL_VOLUME;
|
|
}
|
|
|
|
//
|
|
// Find the next connection in line.
|
|
//
|
|
ulConnection = 0;
|
|
ulEnd =
|
|
FindConnectionToNode
|
|
(
|
|
ulNode,
|
|
ulEnd,
|
|
pSubdeviceDescriptor->Topology,
|
|
&ulConnection,
|
|
&pKsTopologyConnection
|
|
);
|
|
}
|
|
|
|
//
|
|
// Make sure no nodes were missing.
|
|
//
|
|
if (! ulMissing)
|
|
{
|
|
//
|
|
// We have the capabilities required. Build the new format.
|
|
//
|
|
ULONG ulSize =
|
|
( sizeof(KSDATAFORMAT_WAVEFORMATEX)
|
|
+ ( pKsDataFormatIn->FormatSize
|
|
- sizeof(KSDATAFORMAT_DSOUND)
|
|
)
|
|
);
|
|
*ppKsDataFormatOut =
|
|
PKSDATAFORMAT
|
|
(
|
|
ExAllocatePoolWithTag
|
|
(
|
|
PagedPool,
|
|
ulSize,
|
|
'fDcP'
|
|
)
|
|
);
|
|
|
|
if (*ppKsDataFormatOut)
|
|
{
|
|
//
|
|
// Copy KSDATAFORMAT part.
|
|
//
|
|
RtlCopyMemory
|
|
(
|
|
*ppKsDataFormatOut,
|
|
pKsDataFormatIn,
|
|
sizeof(KSDATAFORMAT)
|
|
);
|
|
|
|
//
|
|
// Copy WAVEFORMATEX part including appended stuff.
|
|
//
|
|
RtlCopyMemory
|
|
(
|
|
*ppKsDataFormatOut + 1,
|
|
&pKsDataFormatDSound->BufferDesc.WaveFormatEx,
|
|
ulSize - sizeof(KSDATAFORMAT)
|
|
);
|
|
|
|
//
|
|
// Adjust size and specifier.
|
|
//
|
|
(*ppKsDataFormatOut)->FormatSize = ulSize;
|
|
(*ppKsDataFormatOut)->Specifier =
|
|
KSDATAFORMAT_SPECIFIER_WAVEFORMATEX;
|
|
}
|
|
else
|
|
{
|
|
_DbgPrintF(DEBUGLVL_TERSE,("PcCaptureFormat Failed to allocate memory for format."));
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_DbgPrintF(DEBUGLVL_VERBOSE,("PcCaptureFormat Failed due to lack of feature support (0x%08x).",ulMissing));
|
|
//
|
|
// Don't have the required nodes...fail.
|
|
//
|
|
ntStatus = STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_DbgPrintF(DEBUGLVL_VERBOSE,("PcCaptureFormat Format captured as-is."));
|
|
|
|
//
|
|
// Some other format. Just capture it.
|
|
//
|
|
*ppKsDataFormatOut = PKSDATAFORMAT(ExAllocatePoolWithTag(PagedPool,
|
|
pKsDataFormatIn->FormatSize,
|
|
'fDcP'));
|
|
|
|
if (*ppKsDataFormatOut)
|
|
{
|
|
RtlCopyMemory
|
|
(
|
|
*ppKsDataFormatOut,
|
|
pKsDataFormatIn,
|
|
pKsDataFormatIn->FormatSize
|
|
);
|
|
}
|
|
else
|
|
{
|
|
_DbgPrintF(DEBUGLVL_TERSE,("PcCaptureFormat Failed to allocate memory for format."));
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
// check to verify SampleSize is set properly on waveformatex formats
|
|
if( NT_SUCCESS(ntStatus) &&
|
|
(pKsDataFormatIn->FormatSize >= sizeof(KSDATAFORMAT_WAVEFORMATEX)) &&
|
|
IsEqualGUIDAligned((*ppKsDataFormatOut)->MajorFormat,KSDATAFORMAT_TYPE_AUDIO) &&
|
|
IsEqualGUIDAligned((*ppKsDataFormatOut)->Specifier, KSDATAFORMAT_SPECIFIER_WAVEFORMATEX))
|
|
{
|
|
PKSDATAFORMAT_WAVEFORMATEX pWaveFormat = PKSDATAFORMAT_WAVEFORMATEX(*ppKsDataFormatOut);
|
|
|
|
if( 0 == pWaveFormat->DataFormat.SampleSize )
|
|
{
|
|
pWaveFormat->DataFormat.SampleSize = pWaveFormat->WaveFormatEx.nBlockAlign;
|
|
}
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* PcAcquireFormatResources()
|
|
*****************************************************************************
|
|
* Acquire resources specified in a format.
|
|
*/
|
|
PORTCLASSAPI
|
|
void
|
|
NTAPI
|
|
PcAcquireFormatResources
|
|
(
|
|
IN PKSDATAFORMAT pKsDataFormatIn,
|
|
IN PSUBDEVICE_DESCRIPTOR pSubdeviceDescriptor,
|
|
IN ULONG ulPinId,
|
|
IN PPROPERTY_CONTEXT pPropertyContext
|
|
)
|
|
{
|
|
ASSERT(pKsDataFormatIn);
|
|
ASSERT(pSubdeviceDescriptor);
|
|
ASSERT(ulPinId < pSubdeviceDescriptor->PinCount);
|
|
ASSERT(pPropertyContext);
|
|
|
|
KSP_NODE ksPNode;
|
|
ksPNode.Property.Set = KSPROPSETID_TopologyNode;
|
|
ksPNode.Property.Id = KSPROPERTY_TOPOLOGYNODE_ENABLE;
|
|
ksPNode.Property.Flags = KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_TOPOLOGY;
|
|
ksPNode.NodeId = 0; // Fill in later
|
|
ksPNode.Reserved = 0;
|
|
|
|
if ( (pKsDataFormatIn->FormatSize >= sizeof(KSDATAFORMAT_DSOUND))
|
|
&& IsEqualGUIDAligned
|
|
( pKsDataFormatIn->MajorFormat,
|
|
KSDATAFORMAT_TYPE_AUDIO
|
|
)
|
|
&& IsEqualGUIDAligned
|
|
( pKsDataFormatIn->Specifier,
|
|
KSDATAFORMAT_SPECIFIER_DSOUND
|
|
)
|
|
)
|
|
{
|
|
//
|
|
// This is the dreaded DSound format. Turn on all the nodes
|
|
// that are specified in the caps bits.
|
|
//
|
|
PKSDATAFORMAT_DSOUND pKsDataFormatDSound =
|
|
PKSDATAFORMAT_DSOUND(pKsDataFormatIn);
|
|
|
|
//
|
|
// Find a connection involving the filter pin.
|
|
//
|
|
ULONG ulConnection = 0;
|
|
PKSTOPOLOGY_CONNECTION pKsTopologyConnection;
|
|
ULONG ulEnd =
|
|
FindConnectionToPin
|
|
(
|
|
KSFILTER_NODE,
|
|
ulPinId,
|
|
pSubdeviceDescriptor->Topology,
|
|
&ulConnection,
|
|
&pKsTopologyConnection
|
|
);
|
|
|
|
//
|
|
// Trace the topology until we find a non-transform or all the
|
|
// required nodes have been found. Position notification is
|
|
// always supported.
|
|
//
|
|
ULONG ulMissing =
|
|
( pKsDataFormatDSound->BufferDesc.Control
|
|
& ( KSDSOUND_BUFFER_CTRL_3D
|
|
| KSDSOUND_BUFFER_CTRL_FREQUENCY
|
|
)
|
|
);
|
|
|
|
while (ulMissing && ulEnd)
|
|
{
|
|
//
|
|
// Found a connection. Follow the topology.
|
|
//
|
|
ULONG ulNode = NodeAtOtherEnd(ulEnd,pKsTopologyConnection);
|
|
|
|
if (! NodeIsTransform(ulNode,pSubdeviceDescriptor->Topology))
|
|
{
|
|
//
|
|
// The new node is not a simple transform (1 in, 1 out).
|
|
//
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Turn on nodes as appropriate based on the node GUID.
|
|
//
|
|
ASSERT(ulNode < pSubdeviceDescriptor->Topology->TopologyNodesCount);
|
|
const GUID *pGuid = &pSubdeviceDescriptor->Topology->TopologyNodes[ulNode];
|
|
if (IsEqualGUIDAligned(*pGuid,KSNODETYPE_3D_EFFECTS))
|
|
{
|
|
if (ulMissing & KSDSOUND_BUFFER_CTRL_3D)
|
|
{
|
|
//
|
|
// Turn on the 3D node.
|
|
//
|
|
ULONG ulPropertyValue = TRUE;
|
|
ULONG ulPropertyValueSize = sizeof(ULONG);
|
|
ksPNode.NodeId = ulNode;
|
|
|
|
PcDispatchProperty
|
|
( NULL // pIrp
|
|
, pPropertyContext
|
|
, NULL // pKsPropertySet
|
|
, sizeof(KSP_NODE)
|
|
, &ksPNode.Property
|
|
, &ulPropertyValueSize
|
|
, &ulPropertyValue
|
|
);
|
|
|
|
ulMissing &=~ KSDSOUND_BUFFER_CTRL_3D;
|
|
}
|
|
}
|
|
else
|
|
if (IsEqualGUIDAligned(*pGuid,KSNODETYPE_SRC))
|
|
{
|
|
if (ulMissing & KSDSOUND_BUFFER_CTRL_FREQUENCY)
|
|
{
|
|
//
|
|
// Turn on the SRC node.
|
|
//
|
|
ULONG ulPropertyValue = TRUE;
|
|
ULONG ulPropertyValueSize = sizeof(ULONG);
|
|
ksPNode.NodeId = ulNode;
|
|
|
|
PcDispatchProperty
|
|
( NULL // pIrp
|
|
, pPropertyContext
|
|
, NULL // pKsPropertySet
|
|
, sizeof(KSP_NODE)
|
|
, &ksPNode.Property
|
|
, &ulPropertyValueSize
|
|
, &ulPropertyValue
|
|
);
|
|
|
|
ulMissing &=~ KSDSOUND_BUFFER_CTRL_FREQUENCY;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Find the next connection in line.
|
|
//
|
|
ulConnection = 0;
|
|
ulEnd =
|
|
FindConnectionToNode
|
|
(
|
|
ulNode,
|
|
ulEnd,
|
|
pSubdeviceDescriptor->Topology,
|
|
&ulConnection,
|
|
&pKsTopologyConnection
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
#pragma code_seg()
|
|
|
|
/*****************************************************************************
|
|
* PcRegisterIoTimeout()
|
|
*****************************************************************************
|
|
* Registers an IoTimeout callback.
|
|
*/
|
|
PORTCLASSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
PcRegisterIoTimeout
|
|
(
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIO_TIMER_ROUTINE pTimerRoutine,
|
|
IN PVOID pContext
|
|
)
|
|
{
|
|
KIRQL OldIrql;
|
|
PTIMEOUTCALLBACK TimeoutCallback;
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
|
|
ASSERT(pDeviceObject);
|
|
ASSERT(pTimerRoutine);
|
|
ASSERT( PASSIVE_LEVEL == KeGetCurrentIrql() );
|
|
|
|
//
|
|
// Validate Parameters.
|
|
//
|
|
if (NULL == pDeviceObject ||
|
|
NULL == pTimerRoutine ||
|
|
NULL == pDeviceObject->DeviceExtension)
|
|
{
|
|
_DbgPrintF(DEBUGLVL_TERSE, ("PcRegisterIoTimeout : Invalid Parameter"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
// get the device context
|
|
PDEVICE_CONTEXT pDeviceContext = PDEVICE_CONTEXT(pDeviceObject->DeviceExtension);
|
|
|
|
// allocate a timeout callback structure -- 'PcTc'
|
|
TimeoutCallback = PTIMEOUTCALLBACK(ExAllocatePoolWithTag( NonPagedPool, sizeof(TIMEOUTCALLBACK),'cTcP' ));
|
|
if( TimeoutCallback )
|
|
{
|
|
// initialize the entry
|
|
TimeoutCallback->TimerRoutine = pTimerRoutine;
|
|
TimeoutCallback->Context = pContext;
|
|
|
|
// grab the list spin lock
|
|
KeAcquireSpinLock( &(pDeviceContext->TimeoutLock), &OldIrql );
|
|
|
|
// walk the list to see if the entry is already registered
|
|
if( !IsListEmpty( &(pDeviceContext->TimeoutList) ) )
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
PTIMEOUTCALLBACK pCallback;
|
|
|
|
for( ListEntry = pDeviceContext->TimeoutList.Flink;
|
|
ListEntry != &(pDeviceContext->TimeoutList);
|
|
ListEntry = ListEntry->Flink )
|
|
{
|
|
pCallback = (PTIMEOUTCALLBACK) CONTAINING_RECORD( ListEntry,
|
|
TIMEOUTCALLBACK,
|
|
ListEntry );
|
|
if( (pCallback->TimerRoutine == pTimerRoutine) &&
|
|
(pCallback->Context == pContext) )
|
|
{
|
|
// entry already exists in the list, so fail this request
|
|
ntStatus = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( NT_SUCCESS(ntStatus) )
|
|
{
|
|
// add the entry to the list
|
|
InsertTailList( &(pDeviceContext->TimeoutList), &(TimeoutCallback->ListEntry) );
|
|
}
|
|
else
|
|
{
|
|
// free the entry
|
|
ExFreePool( TimeoutCallback );
|
|
}
|
|
|
|
// release the spin lock
|
|
KeReleaseSpinLock( &(pDeviceContext->TimeoutLock), OldIrql );
|
|
}
|
|
else
|
|
{
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* PcUnregisterIoTimeout()
|
|
*****************************************************************************
|
|
* Unregisters an IoTimeout callback.
|
|
*/
|
|
PORTCLASSAPI
|
|
NTSTATUS
|
|
NTAPI
|
|
PcUnregisterIoTimeout
|
|
(
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIO_TIMER_ROUTINE pTimerRoutine,
|
|
IN PVOID pContext
|
|
)
|
|
{
|
|
KIRQL OldIrql;
|
|
|
|
ASSERT(pDeviceObject);
|
|
ASSERT(pTimerRoutine);
|
|
ASSERT( PASSIVE_LEVEL == KeGetCurrentIrql() );
|
|
|
|
//
|
|
// Validate Parameters.
|
|
//
|
|
if (NULL == pDeviceObject ||
|
|
NULL == pTimerRoutine ||
|
|
NULL == pDeviceObject->DeviceExtension)
|
|
{
|
|
_DbgPrintF(DEBUGLVL_TERSE, ("PcUnregisterIoTimeout : Invalid Parameter"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
// get the device context
|
|
PDEVICE_CONTEXT pDeviceContext = PDEVICE_CONTEXT(pDeviceObject->DeviceExtension);
|
|
|
|
// grab the spin lock
|
|
KeAcquireSpinLock( &(pDeviceContext->TimeoutLock), &OldIrql );
|
|
|
|
// walk the list
|
|
if( !IsListEmpty( &(pDeviceContext->TimeoutList) ) )
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
PTIMEOUTCALLBACK pCallback;
|
|
|
|
for( ListEntry = pDeviceContext->TimeoutList.Flink;
|
|
ListEntry != &(pDeviceContext->TimeoutList);
|
|
ListEntry = ListEntry->Flink )
|
|
{
|
|
pCallback = (PTIMEOUTCALLBACK) CONTAINING_RECORD( ListEntry,
|
|
TIMEOUTCALLBACK,
|
|
ListEntry );
|
|
if( (pCallback->TimerRoutine == pTimerRoutine) &&
|
|
(pCallback->Context == pContext) )
|
|
{
|
|
RemoveEntryList(ListEntry);
|
|
KeReleaseSpinLock( &(pDeviceContext->TimeoutLock), OldIrql );
|
|
ExFreePool(pCallback);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
// release the spinlock
|
|
KeReleaseSpinLock( &(pDeviceContext->TimeoutLock), OldIrql );
|
|
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
|