//--------------------------------------------------------------------------- // // Module: fn.cpp // // Description: // // //@@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" EXTERN_C VOID KeAttachProcess(PVOID); EXTERN_C VOID KeDetachProcess(VOID); //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- PLIST_FILTER_NODE gplstFilterNode = NULL; PLIST_MULTI_LOGICAL_FILTER_NODE gplstLogicalFilterNode = NULL; //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- NTSTATUS InitializeFilterNode() { if(gplstFilterNode == NULL) { gplstFilterNode = new LIST_FILTER_NODE; if(gplstFilterNode == NULL) { return(STATUS_INSUFFICIENT_RESOURCES); } } if(gplstLogicalFilterNode == NULL) { gplstLogicalFilterNode = new LIST_MULTI_LOGICAL_FILTER_NODE; if(gplstLogicalFilterNode == NULL) { return(STATUS_INSUFFICIENT_RESOURCES); } } return(STATUS_SUCCESS); } #pragma PAGEABLE_CODE #pragma PAGEABLE_DATA VOID UninitializeFilterNode() { PFILTER_NODE pFilterNode; FOR_EACH_LIST_ITEM_DELETE(gplstFilterNode, pFilterNode) { if (pFilterNode->pDeviceNode) { pFilterNode->pDeviceNode->pFilterNode=NULL; } delete pFilterNode; DELETE_LIST_ITEM(gplstFilterNode); } END_EACH_LIST_ITEM delete gplstFilterNode; gplstFilterNode = NULL; delete gplstLogicalFilterNode; gplstLogicalFilterNode = NULL; } //--------------------------------------------------------------------------- CFilterNode::CFilterNode( ULONG fulType ) { ASSERT(gplstFilterNode != NULL); SetType(fulType); AddListEnd(gplstFilterNode); DPF2(60, "CFilterNode: %08x %s", this, DumpName()); } CFilterNode::~CFilterNode( ) { PFILTER_NODE pFilterNode; Assert(this); DPF2(60, "~CFilterNode: %08x %s", this, DumpName()); RemoveListCheck(); if (pDeviceNode) { pDeviceNode->pFilterNode = NULL; } if (pFileObject) { ::ObDereferenceObject(pFileObject); pFileObject = NULL; } delete pDeviceNode; FOR_EACH_LIST_ITEM(gplstFilterNode, pFilterNode) { pFilterNode->lstConnectedFilterNode.RemoveList(this); } END_EACH_LIST_ITEM // Free all other memory lstFreeMem.EnumerateList(CListDataItem::Destroy); } NTSTATUS CFilterNode::Create( PWSTR pwstrDeviceInterface ) { PFILTER_NODE_INSTANCE pFilterNodeInstance = NULL; PKEY_VALUE_FULL_INFORMATION pkvfi = NULL; NTSTATUS Status = STATUS_SUCCESS; HANDLE hkeyDeviceClass = NULL; HANDLE hkeySysaudio = NULL; UNICODE_STRING ustrFilterName; KSPROPERTY PropertyComponentId; KSCOMPONENTID ComponentId; ULONG BytesReturned; PFILE_OBJECT pFileObject; this->pwstrDeviceInterface = new WCHAR[wcslen(pwstrDeviceInterface) + 1]; if(this->pwstrDeviceInterface == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto exit; } wcscpy(this->pwstrDeviceInterface, pwstrDeviceInterface); Status = lstFreeMem.AddList(this->pwstrDeviceInterface); if(!NT_SUCCESS(Status)) { Trap(); delete this->pwstrDeviceInterface; goto exit; } Status = CFilterNodeInstance::Create(&pFilterNodeInstance, this); if(!NT_SUCCESS(Status)) { goto exit; } pFileObject = pFilterNodeInstance->pFileObject; // Get the filter's friendly name RtlInitUnicodeString(&ustrFilterName, this->pwstrDeviceInterface); Status = IoOpenDeviceInterfaceRegistryKey( &ustrFilterName, KEY_READ, &hkeyDeviceClass); if(!NT_SUCCESS(Status)) { goto exit; } Status = OpenRegistryKey(L"Sysaudio", &hkeySysaudio, hkeyDeviceClass); if(NT_SUCCESS(Status)) { Status = QueryRegistryValue(hkeySysaudio, L"Disabled", &pkvfi); if(NT_SUCCESS(Status)) { if(pkvfi->Type == REG_DWORD) { if(*((PULONG)(((PUCHAR)pkvfi) + pkvfi->DataOffset))) { Status = STATUS_SERVER_DISABLED; delete pkvfi; goto exit; } } delete pkvfi; } Status = QueryRegistryValue(hkeySysaudio, L"Device", &pkvfi); if(NT_SUCCESS(Status)) { if(pkvfi->Type == REG_SZ && pkvfi->DataLength > 0) { Status = lstFreeMem.AddList(pkvfi); if(!NT_SUCCESS(Status)) { Trap(); delete pkvfi; goto exit; } Status = AddDeviceInterfaceMatch( (PWSTR)(((PUCHAR)pkvfi) + pkvfi->DataOffset)); if(!NT_SUCCESS(Status)) { Trap(); delete pkvfi; goto exit; } } else { delete pkvfi; } } Status = QueryRegistryValue(hkeySysaudio, L"Order", &pkvfi); if(NT_SUCCESS(Status)) { if(pkvfi->Type == REG_DWORD) { ulOrder = *((PULONG)(((PUCHAR)pkvfi) + pkvfi->DataOffset)); } delete pkvfi; } Status = QueryRegistryValue(hkeySysaudio, L"Capture", &pkvfi); if(NT_SUCCESS(Status)) { if(pkvfi->Type == REG_DWORD) { if(*((PULONG)(((PUCHAR)pkvfi) + pkvfi->DataOffset))) { ulFlags |= FN_FLAGS_CAPTURE; } else { ulFlags |= FN_FLAGS_NO_CAPTURE; } } delete pkvfi; } Status = QueryRegistryValue(hkeySysaudio, L"Render", &pkvfi); if(NT_SUCCESS(Status)) { if(pkvfi->Type == REG_DWORD) { if(*((PULONG)(((PUCHAR)pkvfi) + pkvfi->DataOffset))) { ulFlags |= FN_FLAGS_RENDER; } else { ulFlags |= FN_FLAGS_NO_RENDER; } } delete pkvfi; } } Status = QueryRegistryValue(hkeyDeviceClass, L"FriendlyName", &pkvfi); if(NT_SUCCESS(Status)) { if(pkvfi->Type == REG_SZ && pkvfi->DataLength > 0) { Status = lstFreeMem.AddList(pkvfi); if(!NT_SUCCESS(Status)) { Trap(); delete pkvfi; goto exit; } pwstrFriendlyName = (PWSTR)(((PUCHAR)pkvfi) + pkvfi->DataOffset); } else { delete pkvfi; } } // Get the component Id of the filter PropertyComponentId.Set = KSPROPSETID_General; PropertyComponentId.Id = KSPROPERTY_GENERAL_COMPONENTID; PropertyComponentId.Flags = KSPROPERTY_TYPE_GET; Status = KsSynchronousIoControlDevice( pFileObject, KernelMode, IOCTL_KS_PROPERTY, &PropertyComponentId, sizeof(PropertyComponentId), &ComponentId, sizeof(ComponentId), &BytesReturned); // Store the component Id if (NT_SUCCESS(Status)) { ASSERT(BytesReturned >= sizeof(ComponentId)); this->ComponentId = new KSCOMPONENTID; if (this->ComponentId) { RtlCopyMemory(this->ComponentId, &ComponentId, sizeof(KSCOMPONENTID)); Status = lstFreeMem.AddList(this->ComponentId); if(!NT_SUCCESS(Status)) { delete this->ComponentId; this->ComponentId = NULL; } } } else { this->ComponentId = NULL; } Status = this->ProfileFilter(pFileObject); exit: if(hkeySysaudio != NULL) { ZwClose(hkeySysaudio); } if(hkeyDeviceClass != NULL) { ZwClose(hkeyDeviceClass); } if (pFilterNodeInstance) { pFilterNodeInstance->Destroy(); } return(Status); } NTSTATUS CFilterNode::ProfileFilter( PFILE_OBJECT pFileObject ) { NTSTATUS Status = STATUS_SUCCESS; PKSMULTIPLE_ITEM pCategories = NULL; PKSMULTIPLE_ITEM pConnections = NULL; PKSMULTIPLE_ITEM pNodes = NULL; ULONG PinId; PPIN_INFO pPinInfo; ULONG i; KSTOPOLOGY Topology; RtlZeroMemory(&Topology, sizeof(Topology)); // Get the number of pins Status = GetPinProperty( pFileObject, KSPROPERTY_PIN_CTYPES, 0, // doesn't matter for ctypes sizeof(cPins), (PVOID)&cPins); if(!NT_SUCCESS(Status)) { Trap(); goto exit; } // Get the topology of the filter Status = GetProperty( pFileObject, &KSPROPSETID_Topology, KSPROPERTY_TOPOLOGY_CATEGORIES, 0, NULL, (PVOID*)&pCategories); if(!NT_SUCCESS(Status)) { Trap(); goto exit; } if(pCategories != NULL) { Topology.CategoriesCount = pCategories->Count; Topology.Categories = (GUID*)(pCategories + 1); ULONG fulType = 0; for(i = 0; i < pCategories->Count; i++) { GetFilterTypeFromGuid((LPGUID)&Topology.Categories[i], &fulType); } SetType(fulType); } Status = GetProperty( pFileObject, &KSPROPSETID_Topology, KSPROPERTY_TOPOLOGY_NODES, 0, NULL, (PVOID*)&pNodes); if(!NT_SUCCESS(Status)) { Trap(); goto exit; } if(pNodes != NULL) { Status = lstFreeMem.AddList(pNodes); if(!NT_SUCCESS(Status)) { Trap(); delete pNodes; goto exit; } Topology.TopologyNodesCount = pNodes->Count; Topology.TopologyNodes = (GUID*)(pNodes + 1); } Status = GetProperty( pFileObject, &KSPROPSETID_Topology, KSPROPERTY_TOPOLOGY_CONNECTIONS, 0, NULL, (PVOID*)&pConnections); if(!NT_SUCCESS(Status)) { Trap(); goto exit; } if(pConnections != NULL) { Topology.TopologyConnectionsCount = pConnections->Count; Topology.TopologyConnections = (PKSTOPOLOGY_CONNECTION)(pConnections + 1); } // Each pin loop for(PinId = 0; PinId < cPins; PinId++) { pPinInfo = new PIN_INFO(this, PinId); if(pPinInfo == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; Trap(); goto exit; } Status = pPinInfo->Create(pFileObject); if(!NT_SUCCESS(Status)) { goto exit; } } Status = CreateTopology(this, &Topology); if(!NT_SUCCESS(Status)) { goto exit; } Status = lstPinInfo.EnumerateList(CPinInfo::CreatePhysicalConnection); if(Status == STATUS_CONTINUE) { Status = STATUS_SUCCESS; } else if(!NT_SUCCESS(Status)) { goto exit; } Status = CLogicalFilterNode::CreateAll(this); if(!NT_SUCCESS(Status)) { goto exit; } exit: delete pCategories; delete pConnections; return(Status); } NTSTATUS CFilterNode::DuplicateForCapture( ) { PLOGICAL_FILTER_NODE pLogicalFilterNode; NTSTATUS Status = STATUS_SUCCESS; PFILTER_NODE pFilterNode = NULL; if(GetType() & FILTER_TYPE_DUP_FOR_CAPTURE) { FOR_EACH_LIST_ITEM(&lstLogicalFilterNode, pLogicalFilterNode) { if(!pLogicalFilterNode->IsRenderAndCapture()) { ASSERT(NT_SUCCESS(Status)); goto exit; } } END_EACH_LIST_ITEM pFilterNode = new FILTER_NODE(GetType()); if(pFilterNode == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; Trap(); goto exit; } Status = pFilterNode->Create(GetDeviceInterface()); if(!NT_SUCCESS(Status)) { goto exit; } SetRenderOnly(); pFilterNode->SetCaptureOnly(); } exit: if(!NT_SUCCESS(Status)) { Trap(); delete pFilterNode; } return(Status); } BOOL CFilterNode::IsDeviceInterfaceMatch( PDEVICE_NODE pDeviceNode ) { PWSTR pwstr, pwstrDeviceInterface; UNICODE_STRING String1, String2; Assert(this); if(lstwstrDeviceInterfaceMatch.IsLstEmpty()) { return(TRUE); } // // The +4 for both the strings is to eliminate the \\.\ differences in // user mode device interface names & kernel mode device interface names // pwstrDeviceInterface = pDeviceNode->GetDeviceInterface()+4; RtlInitUnicodeString(&String2, pwstrDeviceInterface); FOR_EACH_LIST_ITEM(&lstwstrDeviceInterfaceMatch, pwstr) { RtlInitUnicodeString(&String1, (pwstr+4)); if (RtlEqualUnicodeString(&String1, &String2, TRUE)) { return(TRUE); } } END_EACH_LIST_ITEM return(FALSE); } VOID CFilterNode::SetType( ULONG fulType ) { this->fulType |= fulType; // // This is because of left overs (type bridge) in the registry // that look like aliases. // if(this->fulType & FILTER_TYPE_TOPOLOGY) { this->fulType = (FILTER_TYPE_AUDIO | FILTER_TYPE_TOPOLOGY); } GetDefaultOrder(this->fulType, &ulOrder); } NTSTATUS CFilterNode::CreatePin( PKSPIN_CONNECT pPinConnect, ACCESS_MASK Access, PHANDLE pHandle ) { NTSTATUS Status; ::KeAttachProcess(this->pProcess); Status = KsCreatePin(this->hFileHandle, pPinConnect, Access, pHandle); ::KeDetachProcess(); return(Status); } BOOL CFilterNode::DoesGfxMatch( HANDLE hGfx, PWSTR pwstrDeviceName, ULONG GfxOrder ) { ULONG DeviceCount; UNICODE_STRING usInDevice, usfnDevice; PWSTR pwstr; RtlInitUnicodeString(&usInDevice, (pwstrDeviceName+4)); // // Skip if it is not a GFX // DPF1(90, "DoesGfxMatch:: fultype=%x", this->fulType); if (!(this->fulType & FILTER_TYPE_GFX)) { return(FALSE); } // // If it is a valid handle value, check whether the handle matches // if (hGfx) { if (this->hFileHandle != hGfx) { return(FALSE); } } // // Skip if the order does not match // DPF1(90, "DoesGfxMatch:: order=%x", this->ulOrder); if (GfxOrder != this->ulOrder) { return(FALSE); } // // Skip if the match device list is empty :: should not happen with GFX // if(lstwstrDeviceInterfaceMatch.IsLstEmpty()) { ASSERT(!"GFX with no device to attach to!\n"); return(FALSE); } // // Check if any of the Match device strings matches the device interface // passed in. (In case of GFX we should have only one string though) // DeviceCount = 0; FOR_EACH_LIST_ITEM(&lstwstrDeviceInterfaceMatch, pwstr) { ASSERT(DeviceCount == 0); RtlInitUnicodeString(&usfnDevice, (pwstr+4)); DPF1(95, "DoesGfxMatch:: new di = %s)", DbgUnicode2Sz(pwstrDeviceName)); DPF1(95, "DoesGfxMatch:: old di = %s)", DbgUnicode2Sz(pwstr)); if (RtlEqualUnicodeString(&usInDevice, &usfnDevice, TRUE)) { DPF1(90, "Found a duplicate GFX, pFilterNode = %x", this); return(TRUE); } DeviceCount++; } END_EACH_LIST_ITEM return (FALSE); } //--------------------------------------------------------------------------- #ifdef DEBUG ULONG nFilter = 0; VOID DumpSysAudio( ) { if(gplstFilterNode != NULL) { gplstFilterNode->Dump(); } if(gplstDeviceNode != NULL) { gplstDeviceNode->Dump(); } if(gplstLogicalFilterNode != NULL && (ulDebugFlags & (DEBUG_FLAGS_FILTER | DEBUG_FLAGS_DEVICE | DEBUG_FLAGS_LOGICAL_FILTER)) == DEBUG_FLAGS_LOGICAL_FILTER) { gplstLogicalFilterNode->Dump(); } } ENUMFUNC CFilterNode::Dump( ) { Assert(this); // .sf if(ulDebugFlags & (DEBUG_FLAGS_FILTER | DEBUG_FLAGS_OBJECT)) { if(ulDebugNumber == MAXULONG || ulDebugNumber == nFilter) { if(ulDebugFlags & DEBUG_FLAGS_ADDRESS) { dprintf("%d: %08x %s\n", nFilter, this, DumpName()); } else { dprintf("%d: %s\n", nFilter, DumpName()); } // .sfv if(ulDebugFlags & (DEBUG_FLAGS_VERBOSE | DEBUG_FLAGS_OBJECT)) { dprintf("FN: %08x DN %08x cPins %08x ulOrder %08x\n", this, pDeviceNode, cPins, ulOrder); dprintf(" fulType: %08x ", fulType); DumpfulType(fulType); dprintf("\n ulFlags: %08x ", ulFlags); if(ulFlags & FN_FLAGS_RENDER) { dprintf("RENDER "); } if(ulFlags & FN_FLAGS_NO_RENDER) { dprintf("NO_RENDER "); } if(ulFlags & FN_FLAGS_CAPTURE) { dprintf("CAPTURE "); } if(ulFlags & FN_FLAGS_NO_CAPTURE) { dprintf("NO_CAPTURE "); } dprintf("\n"); if(pwstrDeviceInterface != NULL) { dprintf(" %s\n", DumpDeviceInterface()); } PWSTR pwstr; FOR_EACH_LIST_ITEM(&lstwstrDeviceInterfaceMatch, pwstr) { dprintf(" pwstrDevice:\n %s\n", DbgUnicode2Sz(pwstr)); } END_EACH_LIST_ITEM dprintf(" lstLFN: "); lstLogicalFilterNode.DumpAddress(); dprintf("\n lstConnFN:"); lstConnectedFilterNode.DumpAddress(); dprintf("\n"); } // .sfp if(ulDebugFlags & DEBUG_FLAGS_PIN) { lstPinInfo.Dump(); } // .sft if(ulDebugFlags & DEBUG_FLAGS_TOPOLOGY) { lstTopologyNode.Dump(); // .sftx if(ulDebugFlags & DEBUG_FLAGS_DETAILS) { lstTopologyConnection.Dump(); } } // .sfl if(ulDebugFlags & DEBUG_FLAGS_LOGICAL_FILTER) { lstLogicalFilterNode.Dump(); } if(ulDebugFlags & (DEBUG_FLAGS_VERBOSE | DEBUG_FLAGS_PIN | DEBUG_FLAGS_TOPOLOGY)) { dprintf("\n"); } } nFilter++; } return(STATUS_CONTINUE); } #endif