555 lines
13 KiB
C++
555 lines
13 KiB
C++
//---------------------------------------------------------------------------
|
|
//
|
|
// Module: dn.cpp
|
|
//
|
|
// Description:
|
|
//
|
|
// DeviceNode Class
|
|
//
|
|
//@@BEGIN_MSINTERNAL
|
|
// Development Team:
|
|
// Mike McLaughlin
|
|
//
|
|
// History: Date Author Comment
|
|
//
|
|
// To Do: Date Author Comment
|
|
//
|
|
//@@END_MSINTERNAL
|
|
//
|
|
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
|
|
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
|
|
// PURPOSE.
|
|
//
|
|
// Copyright (c) 1996-1999 Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include "common.h"
|
|
|
|
//---------------------------------------------------------------------------
|
|
//---------------------------------------------------------------------------
|
|
|
|
PLIST_DEVICE_NODE gplstDeviceNode = NULL;
|
|
|
|
//---------------------------------------------------------------------------
|
|
//---------------------------------------------------------------------------
|
|
|
|
#pragma INIT_CODE
|
|
#pragma INIT_DATA
|
|
|
|
NTSTATUS
|
|
InitializeDeviceNode(
|
|
)
|
|
{
|
|
if(gplstDeviceNode == NULL) {
|
|
gplstDeviceNode = new LIST_DEVICE_NODE;
|
|
if(gplstDeviceNode == NULL) {
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
if(gplstConnectNode == NULL) {
|
|
gplstConnectNode = new LIST_CONNECT_NODE;
|
|
if(gplstConnectNode == NULL) {
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
}
|
|
if(gplstPinNodeInstance == NULL) {
|
|
gplstPinNodeInstance = new LIST_PIN_NODE_INSTANCE;
|
|
if(gplstPinNodeInstance == NULL) {
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
}
|
|
if(gplstFilterInstance == NULL) {
|
|
gplstFilterInstance = new LIST_DATA_FILTER_INSTANCE;
|
|
if(gplstFilterInstance == NULL) {
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
}
|
|
#endif
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
#pragma PAGEABLE_CODE
|
|
#pragma PAGEABLE_DATA
|
|
|
|
VOID
|
|
UninitializeDeviceNode(
|
|
)
|
|
{
|
|
delete gplstDeviceNode;
|
|
gplstDeviceNode = NULL;
|
|
#ifdef DEBUG
|
|
ASSERT(gplstConnectNode->IsLstEmpty());
|
|
delete gplstConnectNode;
|
|
gplstConnectNode = NULL;
|
|
ASSERT(gplstPinNodeInstance->IsLstEmpty());
|
|
delete gplstPinNodeInstance;
|
|
gplstPinNodeInstance = NULL;
|
|
delete gplstFilterInstance;
|
|
gplstFilterInstance = NULL;
|
|
#endif
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
CDeviceNode::CDeviceNode(
|
|
)
|
|
{
|
|
ASSERT(gplstDeviceNode != NULL);
|
|
AddListEnd(gplstDeviceNode);
|
|
DPF1(50, "CDeviceNode: %08x", this);
|
|
}
|
|
|
|
CDeviceNode::~CDeviceNode(
|
|
)
|
|
{
|
|
PFILTER_INSTANCE pFilterInstance;
|
|
ULONG i;
|
|
|
|
Assert(this);
|
|
RemoveList();
|
|
if (pFilterNode) {
|
|
pFilterNode->pDeviceNode = NULL;
|
|
}
|
|
|
|
delete pShingleInstance;
|
|
|
|
FOR_EACH_LIST_ITEM_DELETE(&lstFilterInstance, pFilterInstance) {
|
|
ASSERT(pFilterInstance->GetDeviceNode() == this);
|
|
pFilterInstance->SetDeviceNode(NULL);
|
|
} END_EACH_LIST_ITEM
|
|
|
|
if(papVirtualSourceData != NULL) {
|
|
for(i = 0; i < cVirtualSourceData; i++) {
|
|
delete papVirtualSourceData[i];
|
|
}
|
|
delete papVirtualSourceData;
|
|
}
|
|
for(i = 0; i < MAX_SYSAUDIO_DEFAULT_TYPE; i++) {
|
|
if(apShingleInstance[i] != NULL) {
|
|
if(apShingleInstance[i]->GetDeviceNode() == this) {
|
|
apShingleInstance[i]->SetDeviceNode(NULL);
|
|
}
|
|
}
|
|
}
|
|
delete pFilterNodeVirtual;
|
|
DPF1(50, "~CFilterNode: %08x", this);
|
|
}
|
|
|
|
NTSTATUS
|
|
CDeviceNode::Create(
|
|
PFILTER_NODE pFilterNode
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
Assert(this);
|
|
Assert(pFilterNode);
|
|
this->pFilterNode = pFilterNode;
|
|
|
|
Status = Update();
|
|
if(!NT_SUCCESS(Status)) {
|
|
goto exit;
|
|
}
|
|
pShingleInstance = new SHINGLE_INSTANCE(FLAGS_COMBINE_PINS);
|
|
if(pShingleInstance == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit;
|
|
}
|
|
Status = pShingleInstance->Create(this, (LPGUID)&KSCATEGORY_AUDIO_DEVICE);
|
|
if(!NT_SUCCESS(Status)) {
|
|
goto exit;
|
|
}
|
|
exit:
|
|
return(Status);
|
|
}
|
|
|
|
NTSTATUS
|
|
CDeviceNode::Update(
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PFILTER_NODE pFilterNodeNext;
|
|
ULONG i;
|
|
|
|
Assert(this);
|
|
Assert(pFilterNode);
|
|
DPF2(50, "CDeviceNode::Update DN %08x %s", this, DumpName());
|
|
|
|
lstGraphNode.DestroyList();
|
|
lstLogicalFilterNode.DestroyList();
|
|
delete pFilterNodeVirtual;
|
|
pFilterNodeVirtual = NULL;
|
|
|
|
if(papVirtualSourceData != NULL) {
|
|
for(i = 0; i < cVirtualSourceData; i++) {
|
|
delete papVirtualSourceData[i];
|
|
}
|
|
delete papVirtualSourceData;
|
|
papVirtualSourceData = NULL;
|
|
}
|
|
|
|
if(gcVirtualSources != 0) {
|
|
papVirtualSourceData = new PVIRTUAL_SOURCE_DATA[gcVirtualSources];
|
|
if(papVirtualSourceData == NULL) {
|
|
Trap();
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit;
|
|
}
|
|
for(i = 0; i < gcVirtualSources; i++) {
|
|
papVirtualSourceData[i] = new VIRTUAL_SOURCE_DATA(this);
|
|
if(papVirtualSourceData[i] == NULL) {
|
|
Trap();
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
cVirtualSourceData = gcVirtualSources;
|
|
|
|
Status = AddLogicalFilterNode(pFilterNode);
|
|
if(!NT_SUCCESS(Status)) {
|
|
Trap();
|
|
goto exit;
|
|
}
|
|
|
|
FOR_EACH_LIST_ITEM(&pFilterNode->lstConnectedFilterNode, pFilterNodeNext) {
|
|
|
|
Status = AddLogicalFilterNode(pFilterNodeNext);
|
|
if(!NT_SUCCESS(Status)) {
|
|
Trap();
|
|
goto exit;
|
|
}
|
|
|
|
} END_EACH_LIST_ITEM
|
|
|
|
Status = CreateVirtualMixer(this);
|
|
if(!NT_SUCCESS(Status)) {
|
|
Trap();
|
|
goto exit;
|
|
}
|
|
|
|
if(pShingleInstance != NULL) {
|
|
Status = pShingleInstance->SetDeviceNode(this);
|
|
if(!NT_SUCCESS(Status)) {
|
|
Trap();
|
|
goto exit;
|
|
}
|
|
}
|
|
exit:
|
|
return(Status);
|
|
}
|
|
|
|
NTSTATUS
|
|
CDeviceNode::AddLogicalFilterNode(
|
|
PFILTER_NODE pFilterNode
|
|
)
|
|
{
|
|
PLOGICAL_FILTER_NODE pLogicalFilterNode;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
Status = VirtualizeTopology(this, pFilterNode);
|
|
if(!NT_SUCCESS(Status)) {
|
|
Trap();
|
|
goto exit;
|
|
}
|
|
FOR_EACH_LIST_ITEM(
|
|
&pFilterNode->lstLogicalFilterNode,
|
|
pLogicalFilterNode) {
|
|
|
|
DPF2(60, "AddLogicalFilterNode: %08x, DN: %08x",
|
|
pLogicalFilterNode,
|
|
this);
|
|
|
|
Status = pLogicalFilterNode->AddList(&lstLogicalFilterNode);
|
|
if(!NT_SUCCESS(Status)) {
|
|
Trap();
|
|
goto exit;
|
|
}
|
|
pLogicalFilterNode->RemoveList(gplstLogicalFilterNode);
|
|
|
|
} END_EACH_LIST_ITEM
|
|
exit:
|
|
return(Status);
|
|
}
|
|
|
|
NTSTATUS
|
|
CDeviceNode::CreateGraphNodes(
|
|
)
|
|
{
|
|
PGRAPH_NODE pGraphNode, pGraphNodeMixer;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
Assert(this);
|
|
if(lstGraphNode.IsLstEmpty()) {
|
|
|
|
pGraphNode = new GRAPH_NODE(this, 0);
|
|
if(pGraphNode == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
Trap();
|
|
goto exit;
|
|
}
|
|
Status = pGraphNode->Create();
|
|
if(!NT_SUCCESS(Status)) {
|
|
Trap();
|
|
goto exit;
|
|
}
|
|
//
|
|
// Create a special GraphNode that points to the same renderer or
|
|
// capturer, but is marked with the "mixer topology" flag so the
|
|
// pins and topology created for this GraphNode is the virtual mixer
|
|
// topology for the mixer driver.
|
|
//
|
|
pGraphNodeMixer = new GRAPH_NODE(this, FLAGS_MIXER_TOPOLOGY);
|
|
if(pGraphNodeMixer == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
Trap();
|
|
goto exit;
|
|
}
|
|
Status = pGraphNodeMixer->Create();
|
|
if(!NT_SUCCESS(Status)) {
|
|
Trap();
|
|
goto exit;
|
|
}
|
|
}
|
|
exit:
|
|
if(!NT_SUCCESS(Status)) {
|
|
Trap();
|
|
lstGraphNode.DestroyList();
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
NTSTATUS
|
|
CDeviceNode::GetIndexByDevice(
|
|
OUT PULONG pIndex
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
PDEVICE_NODE pDeviceNode;
|
|
UINT iDevice;
|
|
|
|
if(this == NULL) {
|
|
ASSERT(Status == STATUS_INVALID_DEVICE_REQUEST);
|
|
goto exit;
|
|
}
|
|
|
|
iDevice = 0;
|
|
FOR_EACH_LIST_ITEM(gplstDeviceNode, pDeviceNode) {
|
|
|
|
if(pDeviceNode == this) { // This is the one!
|
|
*pIndex = iDevice;
|
|
Status = STATUS_SUCCESS;
|
|
goto exit;
|
|
}
|
|
iDevice++;
|
|
|
|
} END_EACH_LIST_ITEM
|
|
|
|
ASSERT(Status == STATUS_INVALID_DEVICE_REQUEST);
|
|
exit:
|
|
return(Status);
|
|
}
|
|
|
|
VOID
|
|
CDeviceNode::SetPreferredStatus(
|
|
KSPROPERTY_SYSAUDIO_DEFAULT_TYPE DeviceType,
|
|
BOOL Enable
|
|
)
|
|
{
|
|
PFILTER_NODE_INSTANCE pFilterNodeInstance=NULL;
|
|
KSAUDIO_PREFERRED_STATUS PreferredStatus;
|
|
PFILE_OBJECT pFileObject;
|
|
KSPROPERTY PreferredStatusProperty;
|
|
NTSTATUS Status;
|
|
ULONG BytesReturned;
|
|
|
|
Status = CFilterNodeInstance::Create(&pFilterNodeInstance, this->pFilterNode);
|
|
if (!NT_SUCCESS(Status)) {
|
|
DPF1(0, "SetPreferredStatus : Create filterinstance failed with status = 0x%08x", Status);
|
|
goto exit;
|
|
}
|
|
pFileObject = pFilterNodeInstance->pFileObject;
|
|
|
|
ASSERT(pFileObject);
|
|
|
|
//
|
|
// Form the IOCTL packet & send it down
|
|
//
|
|
PreferredStatusProperty.Set = KSPROPSETID_Audio;
|
|
PreferredStatusProperty.Id = KSPROPERTY_AUDIO_PREFERRED_STATUS;
|
|
PreferredStatusProperty.Flags = KSPROPERTY_TYPE_SET;
|
|
|
|
PreferredStatus.Enable = Enable;
|
|
PreferredStatus.DeviceType = DeviceType;
|
|
PreferredStatus.Flags = 0;
|
|
PreferredStatus.Reserved = 0;
|
|
|
|
DPF(60,"Sending preferred Status to:");
|
|
DPF1(60," FriendlyName = %s", DbgUnicode2Sz(this->pFilterNode->GetFriendlyName()));
|
|
DPF1(60," DI = %s", DbgUnicode2Sz(this->pFilterNode->GetDeviceInterface()));
|
|
DPF1(60," Enable = 0x%08x", Enable);
|
|
DPF1(60," DeviceType = 0x%08x", DeviceType);
|
|
|
|
//
|
|
// We actually throw away the status we got back from the device.
|
|
// Even if this failed we will still continue setting the device to be the
|
|
// preferred device
|
|
//
|
|
Status = KsSynchronousIoControlDevice(pFileObject,
|
|
KernelMode,
|
|
IOCTL_KS_PROPERTY,
|
|
&PreferredStatusProperty,
|
|
sizeof(PreferredStatusProperty),
|
|
&PreferredStatus,
|
|
sizeof(PreferredStatus),
|
|
&BytesReturned);
|
|
|
|
|
|
exit:
|
|
if (pFilterNodeInstance) {
|
|
pFilterNodeInstance->Destroy();
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
GetDeviceByIndex(
|
|
IN UINT Index,
|
|
OUT PDEVICE_NODE *ppDeviceNode
|
|
)
|
|
{
|
|
PDEVICE_NODE pDeviceNode;
|
|
NTSTATUS Status;
|
|
UINT iDevice;
|
|
|
|
iDevice = 0;
|
|
FOR_EACH_LIST_ITEM(gplstDeviceNode, pDeviceNode) {
|
|
|
|
if(iDevice++ == Index) { // This is the one!
|
|
*ppDeviceNode = pDeviceNode;
|
|
Status = STATUS_SUCCESS;
|
|
goto exit;
|
|
}
|
|
|
|
} END_EACH_LIST_ITEM
|
|
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
exit:
|
|
return(Status);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
VOID
|
|
DestroyAllGraphs(
|
|
)
|
|
{
|
|
PDEVICE_NODE pDeviceNode;
|
|
|
|
DPF(50, "DestroyAllGraphs");
|
|
|
|
FOR_EACH_LIST_ITEM(gplstDeviceNode, pDeviceNode) {
|
|
pDeviceNode->lstGraphNode.DestroyList();
|
|
} END_EACH_LIST_ITEM
|
|
|
|
#ifdef DEBUG
|
|
ASSERT(gplstConnectNode->IsLstEmpty());
|
|
ASSERT(gplstPinNodeInstance->IsLstEmpty());
|
|
#endif
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
#ifdef DEBUG
|
|
|
|
ULONG nDevice = 0;
|
|
|
|
ENUMFUNC
|
|
CDeviceNode::Dump()
|
|
{
|
|
// .sd .si .sg
|
|
if(ulDebugFlags &
|
|
(DEBUG_FLAGS_DEVICE |
|
|
DEBUG_FLAGS_INSTANCE |
|
|
DEBUG_FLAGS_GRAPH |
|
|
DEBUG_FLAGS_OBJECT)) {
|
|
if(ulDebugNumber == MAXULONG || ulDebugNumber == nDevice) {
|
|
if(ulDebugFlags & DEBUG_FLAGS_ADDRESS) {
|
|
dprintf("%d: %08x %s\n", nDevice, this, DumpName());
|
|
}
|
|
else {
|
|
dprintf("%d: %s\n", nDevice, DumpName());
|
|
}
|
|
if(ulDebugFlags & (DEBUG_FLAGS_DEVICE | DEBUG_FLAGS_OBJECT)) {
|
|
// .sdv
|
|
if(ulDebugFlags & (DEBUG_FLAGS_VERBOSE | DEBUG_FLAGS_OBJECT)) {
|
|
dprintf("DN: %08x FN %08x SHI %08x FNV %08x\n",
|
|
this,
|
|
pFilterNode,
|
|
pShingleInstance,
|
|
pFilterNodeVirtual);
|
|
dprintf(" %s\n", DumpDeviceInterface());
|
|
// .sdv no l
|
|
if((ulDebugFlags & DEBUG_FLAGS_LOGICAL_FILTER) == 0) {
|
|
dprintf(" lstLFN:");
|
|
lstLogicalFilterNode.DumpAddress();
|
|
dprintf("\n");
|
|
}
|
|
// .sdv no g
|
|
if((ulDebugFlags & DEBUG_FLAGS_GRAPH) == 0) {
|
|
dprintf(" lstGN:");
|
|
lstGraphNode.DumpAddress();
|
|
dprintf("\n");
|
|
}
|
|
// .sdv no i
|
|
if((ulDebugFlags & DEBUG_FLAGS_INSTANCE) == 0) {
|
|
dprintf(" lstFI:");
|
|
lstFilterInstance.DumpAddress();
|
|
dprintf("\n");
|
|
}
|
|
dprintf(" papVSD: ");
|
|
// .sdvx
|
|
if(ulDebugFlags & DEBUG_FLAGS_DETAILS) {
|
|
dprintf("\n");
|
|
for(ULONG i = 0; i < cVirtualSourceData; i++) {
|
|
papVirtualSourceData[i]->Dump();
|
|
}
|
|
}
|
|
else {
|
|
for(ULONG i = 0; i < cVirtualSourceData; i++) {
|
|
dprintf("%08x ", papVirtualSourceData[i]);
|
|
}
|
|
dprintf("\n");
|
|
}
|
|
}
|
|
// .sdl
|
|
if(ulDebugFlags & DEBUG_FLAGS_LOGICAL_FILTER) {
|
|
lstLogicalFilterNode.Dump();
|
|
}
|
|
}
|
|
// .sg
|
|
if(ulDebugFlags & DEBUG_FLAGS_GRAPH) {
|
|
lstGraphNode.Dump();
|
|
}
|
|
// .si
|
|
if(ulDebugFlags & DEBUG_FLAGS_INSTANCE) {
|
|
lstFilterInstance.Dump();
|
|
}
|
|
if(ulDebugFlags &
|
|
(DEBUG_FLAGS_VERBOSE | DEBUG_FLAGS_PIN | DEBUG_FLAGS_TOPOLOGY)) {
|
|
dprintf("\n");
|
|
}
|
|
}
|
|
nDevice++;
|
|
}
|
|
return(STATUS_CONTINUE);
|
|
}
|
|
|
|
#endif
|
|
|
|
//---------------------------------------------------------------------------
|
|
|