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.
896 lines
26 KiB
896 lines
26 KiB
//---------------------------------------------------------------------------
|
|
//
|
|
// Module: notify.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"
|
|
|
|
//
|
|
// Include safe string library for safe string manipulation.
|
|
//
|
|
#define STRSAFE_NO_DEPRECATE // Use safe and unsafe functions interchangeably.
|
|
#include "strsafe.h"
|
|
|
|
#define DEVICE_NAME_TAG L"\\\\?\\"
|
|
|
|
//---------------------------------------------------------------------------
|
|
//---------------------------------------------------------------------------
|
|
|
|
CONST GUID *apguidCategories[] = {
|
|
&KSCATEGORY_AUDIO,
|
|
&KSCATEGORY_AUDIO_GFX,
|
|
&KSCATEGORY_TOPOLOGY,
|
|
&KSCATEGORY_BRIDGE,
|
|
&KSCATEGORY_RENDER,
|
|
&KSCATEGORY_CAPTURE,
|
|
&KSCATEGORY_MIXER,
|
|
&KSCATEGORY_DATATRANSFORM,
|
|
&KSCATEGORY_ACOUSTIC_ECHO_CANCEL,
|
|
&KSCATEGORY_INTERFACETRANSFORM,
|
|
&KSCATEGORY_MEDIUMTRANSFORM,
|
|
&KSCATEGORY_DATACOMPRESSOR,
|
|
&KSCATEGORY_DATADECOMPRESSOR,
|
|
&KSCATEGORY_COMMUNICATIONSTRANSFORM,
|
|
&KSCATEGORY_SPLITTER,
|
|
&KSCATEGORY_AUDIO_SPLITTER,
|
|
&KSCATEGORY_SYNTHESIZER,
|
|
&KSCATEGORY_DRM_DESCRAMBLE,
|
|
&KSCATEGORY_MICROPHONE_ARRAY_PROCESSOR,
|
|
};
|
|
|
|
ULONG aulFilterType[] = {
|
|
FILTER_TYPE_AUDIO,
|
|
FILTER_TYPE_GFX,
|
|
FILTER_TYPE_TOPOLOGY,
|
|
FILTER_TYPE_BRIDGE,
|
|
FILTER_TYPE_RENDERER,
|
|
FILTER_TYPE_CAPTURER,
|
|
FILTER_TYPE_MIXER,
|
|
FILTER_TYPE_DATA_TRANSFORM,
|
|
FILTER_TYPE_AEC,
|
|
FILTER_TYPE_INTERFACE_TRANSFORM,
|
|
FILTER_TYPE_MEDIUM_TRANSFORM,
|
|
FILTER_TYPE_DATA_TRANSFORM,
|
|
FILTER_TYPE_DATA_TRANSFORM,
|
|
FILTER_TYPE_COMMUNICATION_TRANSFORM,
|
|
FILTER_TYPE_SPLITTER,
|
|
FILTER_TYPE_SPLITTER,
|
|
FILTER_TYPE_SYNTHESIZER,
|
|
FILTER_TYPE_DRM_DESCRAMBLE,
|
|
FILTER_TYPE_MIC_ARRAY_PROCESSOR,
|
|
};
|
|
|
|
PVOID pNotificationHandle = NULL;
|
|
|
|
//---------------------------------------------------------------------------
|
|
//---------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
RegisterForPlugPlayNotifications(
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
DPF(50, "RegisterForPlugPlayNotifications");
|
|
ASSERT(gpDeviceInstance != NULL);
|
|
ASSERT(gpDeviceInstance->pPhysicalDeviceObject != NULL);
|
|
|
|
Status = IoRegisterPlugPlayNotification(
|
|
EventCategoryDeviceInterfaceChange,
|
|
PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
|
|
(LPGUID)&KSCATEGORY_AUDIO,
|
|
gpDeviceInstance->pPhysicalDeviceObject->DriverObject,
|
|
(NTSTATUS (*)(PVOID, PVOID)) AudioDeviceInterfaceNotification,
|
|
NULL,
|
|
&pNotificationHandle);
|
|
|
|
if(!NT_SUCCESS(Status)) {
|
|
Trap();
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
return(Status);
|
|
}
|
|
|
|
VOID
|
|
UnregisterForPlugPlayNotifications(
|
|
)
|
|
{
|
|
if(pNotificationHandle != NULL) {
|
|
IoUnregisterPlugPlayNotification(pNotificationHandle);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
DecrementAddRemoveCount(
|
|
)
|
|
{
|
|
if(InterlockedDecrement(&glPendingAddDelete) == 0) {
|
|
DPF(50, "DecrementAddRemoveCount: sending event");
|
|
KsGenerateEventList(
|
|
NULL,
|
|
KSEVENT_SYSAUDIO_ADDREMOVE_DEVICE,
|
|
&gEventQueue,
|
|
KSEVENTS_SPINLOCK,
|
|
&gEventLock);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
AddFilterWorker(
|
|
PWSTR pwstrDeviceInterface,
|
|
PVOID pReference
|
|
)
|
|
{
|
|
AddFilter(pwstrDeviceInterface, NULL);
|
|
ExFreePool(pwstrDeviceInterface);
|
|
DecrementAddRemoveCount();
|
|
|
|
// Dereference sysaudio PDO.
|
|
KsDereferenceSoftwareBusObject(gpDeviceInstance->pDeviceHeader);
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
NTSTATUS
|
|
DeleteFilterWorker(
|
|
PWSTR pwstrDeviceInterface,
|
|
PVOID pReference
|
|
)
|
|
{
|
|
DeleteFilter(pwstrDeviceInterface);
|
|
ExFreePool(pwstrDeviceInterface);
|
|
DecrementAddRemoveCount();
|
|
|
|
// Dereference sysaudio PDO.
|
|
KsDereferenceSoftwareBusObject(gpDeviceInstance->pDeviceHeader);
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
NTSTATUS
|
|
AudioDeviceInterfaceNotification(
|
|
IN PDEVICE_INTERFACE_CHANGE_NOTIFICATION pNotification,
|
|
IN PVOID Context
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PWSTR pwstrDeviceInterface;
|
|
|
|
DPF1(50, "AudioDeviceInterfaceNotification: (%s)",
|
|
DbgUnicode2Sz(pNotification->SymbolicLinkName->Buffer));
|
|
|
|
//
|
|
// SECURITY NOTE:
|
|
// We trust the Buffer, because it is passed to us as part of notification
|
|
// from PnP subsystem.
|
|
//
|
|
pwstrDeviceInterface = (PWSTR)
|
|
ExAllocatePoolWithTag(
|
|
PagedPool,
|
|
(wcslen(pNotification->SymbolicLinkName->Buffer) + 1) * sizeof(WCHAR),
|
|
POOLTAG_SYSA);
|
|
if(pwstrDeviceInterface == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit;
|
|
}
|
|
|
|
// The notification sends null terminated unicode strings
|
|
wcscpy(pwstrDeviceInterface, pNotification->SymbolicLinkName->Buffer);
|
|
|
|
if(IsEqualGUID(&pNotification->Event, &GUID_DEVICE_INTERFACE_ARRIVAL)) {
|
|
//
|
|
// Keep a reference so that SWENUM does not REMOVE the device
|
|
// when the Worker thread is running.
|
|
// If the thread is scheduled successfully, it will remove the reference
|
|
// when exiting.
|
|
//
|
|
Status = KsReferenceSoftwareBusObject(gpDeviceInstance->pDeviceHeader);
|
|
if(!NT_SUCCESS(Status)) {
|
|
goto exit;
|
|
}
|
|
|
|
InterlockedIncrement(&glPendingAddDelete);
|
|
Status = QueueWorkList(
|
|
(UTIL_PFN)AddFilterWorker,
|
|
pwstrDeviceInterface,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
KsDereferenceSoftwareBusObject(gpDeviceInstance->pDeviceHeader);
|
|
|
|
}
|
|
}
|
|
else if(IsEqualGUID(&pNotification->Event, &GUID_DEVICE_INTERFACE_REMOVAL)) {
|
|
//
|
|
// Keep a reference so that SWENUM does not REMOVE the device
|
|
// when the Worker thread is running.
|
|
// If the thread is scheduled successfully, it will remove the reference
|
|
// when exiting.
|
|
//
|
|
Status = KsReferenceSoftwareBusObject(gpDeviceInstance->pDeviceHeader);
|
|
if(!NT_SUCCESS(Status)) {
|
|
goto exit;
|
|
}
|
|
|
|
InterlockedIncrement(&glPendingAddDelete);
|
|
Status = QueueWorkList(
|
|
(UTIL_PFN)DeleteFilterWorker,
|
|
pwstrDeviceInterface,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
KsDereferenceSoftwareBusObject(gpDeviceInstance->pDeviceHeader);
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
// SECURITY NOTE:
|
|
// Sysaudio is registering only for EventCategoryDeviceInterfaceChange.
|
|
// This should send ARRIVAL and REMOVAL.
|
|
// If anything else comes up, we will return SUCCESS.
|
|
// However we are making sure that pwstrDeviceInterface is not leaked.
|
|
//
|
|
if (pwstrDeviceInterface) {
|
|
ExFreePool(pwstrDeviceInterface);
|
|
pwstrDeviceInterface = NULL;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (pwstrDeviceInterface) {
|
|
ExFreePool(pwstrDeviceInterface);
|
|
pwstrDeviceInterface = NULL;
|
|
}
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
AddFilter(
|
|
PWSTR pwstrDeviceInterface,
|
|
PFILTER_NODE *ppFilterNode // if !NULL, physical connection addfilter
|
|
)
|
|
{
|
|
PFILTER_NODE pFilterNodeDuplicate = NULL;
|
|
PFILTER_NODE pFilterNode = NULL;
|
|
UNICODE_STRING ustrFilterName;
|
|
UNICODE_STRING ustrAliasName;
|
|
UNICODE_STRING ustrName;
|
|
NTSTATUS Status;
|
|
ULONG fulType;
|
|
int i;
|
|
|
|
DPF1(50, "AddFilter: (%s)", DbgUnicode2Sz(pwstrDeviceInterface));
|
|
|
|
fulType = 0;
|
|
RtlInitUnicodeString(&ustrFilterName, pwstrDeviceInterface);
|
|
|
|
//
|
|
// For each Interface in apguidCategories, get interface alias of
|
|
// the new device. Check for duplicate interfaces.
|
|
//
|
|
for(i = 0; i < SIZEOF_ARRAY(apguidCategories); i++) {
|
|
Status = IoGetDeviceInterfaceAlias(
|
|
&ustrFilterName,
|
|
apguidCategories[i],
|
|
&ustrAliasName);
|
|
|
|
if(NT_SUCCESS(Status)) {
|
|
HANDLE hAlias;
|
|
|
|
Status = OpenDevice(ustrAliasName.Buffer, &hAlias);
|
|
|
|
if(NT_SUCCESS(Status)) {
|
|
DPF2(100, "AddFilter: alias (%s) aulFilterType %08x",
|
|
DbgUnicode2Sz(ustrAliasName.Buffer),
|
|
aulFilterType[i]);
|
|
|
|
fulType |= aulFilterType[i];
|
|
ZwClose(hAlias);
|
|
|
|
if(pFilterNodeDuplicate == NULL) {
|
|
FOR_EACH_LIST_ITEM(gplstFilterNode, pFilterNode) {
|
|
if(pFilterNode->GetDeviceInterface() == NULL) {
|
|
continue;
|
|
}
|
|
RtlInitUnicodeString(
|
|
&ustrName,
|
|
pFilterNode->GetDeviceInterface());
|
|
|
|
if(RtlEqualUnicodeString(
|
|
&ustrAliasName,
|
|
&ustrName,
|
|
TRUE)) {
|
|
DPF(50, "AddFilter: dup");
|
|
pFilterNodeDuplicate = pFilterNode;
|
|
break;
|
|
}
|
|
} END_EACH_LIST_ITEM
|
|
}
|
|
}
|
|
else {
|
|
DPF1(10, "AddFilter: OpenDevice FAILED on alias (%s)",
|
|
DbgUnicode2Sz(ustrAliasName.Buffer));
|
|
}
|
|
RtlFreeUnicodeString(&ustrAliasName);
|
|
}
|
|
}
|
|
|
|
pFilterNode = pFilterNodeDuplicate;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Create a new Filter_Node if this is not a duplicate.
|
|
//
|
|
if(pFilterNodeDuplicate == NULL) {
|
|
pFilterNode = new FILTER_NODE(fulType);
|
|
if(pFilterNode == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
Trap();
|
|
goto exit;
|
|
}
|
|
Status = pFilterNode->Create(pwstrDeviceInterface);
|
|
if(!NT_SUCCESS(Status)) {
|
|
goto exit;
|
|
}
|
|
Status = pFilterNode->DuplicateForCapture();
|
|
if(!NT_SUCCESS(Status)) {
|
|
goto exit;
|
|
}
|
|
DPF1(50, "AddFilter: new CFilterNode fulType %08x", fulType);
|
|
}
|
|
|
|
//
|
|
// If this is called from Interface Notification Callback,
|
|
// create a new DeviceNode for the new FilterNode.
|
|
//
|
|
if(ppFilterNode == NULL) {
|
|
if(pFilterNode->GetType() & FILTER_TYPE_ENDPOINT) {
|
|
|
|
//
|
|
// Check if a DeviceNode has already been created for
|
|
// this FilterNode.
|
|
//
|
|
if (NULL != pFilterNodeDuplicate &&
|
|
NULL != pFilterNodeDuplicate->pDeviceNode) {
|
|
DPF1(5, "Duplicate FilterNode %X. Skip DeviceNode Create",
|
|
pFilterNode);
|
|
}
|
|
else {
|
|
pFilterNode->pDeviceNode = new DEVICE_NODE;
|
|
if(pFilterNode->pDeviceNode == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
Trap();
|
|
goto exit;
|
|
}
|
|
|
|
Status = pFilterNode->pDeviceNode->Create(pFilterNode);
|
|
if(!NT_SUCCESS(Status)) {
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
DPF(50, "AddFilter: DestroyAllGraphs");
|
|
DestroyAllGraphs();
|
|
}
|
|
}
|
|
|
|
exit:
|
|
if(!NT_SUCCESS(Status)) {
|
|
DPF2(5, "AddFilter: FAILED (%s) %08x",
|
|
DbgUnicode2Sz(pwstrDeviceInterface),
|
|
Status);
|
|
|
|
if(pFilterNode != NULL && pFilterNodeDuplicate == NULL) {
|
|
delete pFilterNode;
|
|
pFilterNode = NULL;
|
|
}
|
|
}
|
|
if(ppFilterNode != NULL) {
|
|
*ppFilterNode = pFilterNode;
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
NTSTATUS
|
|
DeleteFilter(
|
|
PWSTR pwstrDeviceInterface
|
|
)
|
|
{
|
|
UNICODE_STRING ustrFilterName;
|
|
UNICODE_STRING ustrAliasName;
|
|
UNICODE_STRING ustrName;
|
|
PFILTER_NODE pFilterNode;
|
|
NTSTATUS Status;
|
|
int i;
|
|
|
|
DPF1(50, "DeleteFilter: (%s)", DbgUnicode2Sz(pwstrDeviceInterface));
|
|
|
|
RtlInitUnicodeString(&ustrFilterName, pwstrDeviceInterface);
|
|
|
|
//
|
|
// First delete all filter nodes which have the device interface which is
|
|
// going away
|
|
//
|
|
FOR_EACH_LIST_ITEM_DELETE(gplstFilterNode, pFilterNode) {
|
|
if(pFilterNode->GetDeviceInterface() == NULL) {
|
|
continue;
|
|
}
|
|
RtlInitUnicodeString(
|
|
&ustrName,
|
|
pFilterNode->GetDeviceInterface());
|
|
|
|
if(RtlEqualUnicodeString(
|
|
&ustrFilterName,
|
|
&ustrName,
|
|
TRUE)) {
|
|
delete pFilterNode;
|
|
DELETE_LIST_ITEM(gplstFilterNode);
|
|
}
|
|
} END_EACH_LIST_ITEM
|
|
|
|
for(i = 0; i < SIZEOF_ARRAY(apguidCategories); i++) {
|
|
|
|
//
|
|
// According to PnP group, it is perfectly safe to ask for aliases
|
|
// during removal. The interface itself will be enabled or disabled. But
|
|
// we will still get the correct aliases.
|
|
//
|
|
Status = IoGetDeviceInterfaceAlias(
|
|
&ustrFilterName,
|
|
apguidCategories[i],
|
|
&ustrAliasName);
|
|
|
|
if(NT_SUCCESS(Status)) {
|
|
FOR_EACH_LIST_ITEM_DELETE(gplstFilterNode, pFilterNode) {
|
|
|
|
if(pFilterNode->GetDeviceInterface() == NULL) {
|
|
continue;
|
|
}
|
|
RtlInitUnicodeString(
|
|
&ustrName,
|
|
pFilterNode->GetDeviceInterface());
|
|
|
|
if(RtlEqualUnicodeString(
|
|
&ustrAliasName,
|
|
&ustrName,
|
|
TRUE)) {
|
|
delete pFilterNode;
|
|
DELETE_LIST_ITEM(gplstFilterNode);
|
|
}
|
|
|
|
} END_EACH_LIST_ITEM
|
|
|
|
RtlFreeUnicodeString(&ustrAliasName);
|
|
}
|
|
}
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
#define GFX_VERBOSE_LEVEL 50
|
|
|
|
//=============================================================================
|
|
// Assumptions:
|
|
// - SysaudioGfx.ulType has been already validated.
|
|
//
|
|
NTSTATUS AddGfx(
|
|
PSYSAUDIO_GFX pSysaudioGfx,
|
|
ULONG cbMaxLength
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PFILE_OBJECT pFileObject;
|
|
PFILTER_NODE pFilterNode;
|
|
ULONG Flags;
|
|
PWSTR pwstrDeviceName;
|
|
ULONG GfxOrderBase, GfxOrderCeiling;
|
|
|
|
ASSERT(pSysaudioGfx);
|
|
|
|
pFileObject = NULL;
|
|
pwstrDeviceName = NULL;
|
|
pFilterNode = NULL;
|
|
GfxOrderBase = GfxOrderCeiling = 0;
|
|
|
|
DPF1(GFX_VERBOSE_LEVEL, "AddGfx :: Request to add Gfx %x", pSysaudioGfx);
|
|
DPF1(GFX_VERBOSE_LEVEL, " hGfx = %x", pSysaudioGfx->hGfx);
|
|
DPF1(GFX_VERBOSE_LEVEL, " ulOrder = %x", pSysaudioGfx->ulOrder);
|
|
DPF1(GFX_VERBOSE_LEVEL, " ulType = %x", pSysaudioGfx->ulType);
|
|
DPF1(GFX_VERBOSE_LEVEL, " Flags = %x", pSysaudioGfx->ulFlags);
|
|
|
|
//
|
|
// Setup GFX Order's base & ceiling for future usage
|
|
//
|
|
if (pSysaudioGfx->ulType == GFX_DEVICETYPE_RENDER) {
|
|
GfxOrderBase = ORDER_RENDER_GFX_FIRST;
|
|
GfxOrderCeiling = ORDER_RENDER_GFX_LAST;
|
|
}
|
|
|
|
if (pSysaudioGfx->ulType == GFX_DEVICETYPE_CAPTURE) {
|
|
GfxOrderBase = ORDER_CAPTURE_GFX_FIRST;
|
|
GfxOrderCeiling = ORDER_CAPTURE_GFX_LAST;
|
|
}
|
|
|
|
ASSERT(GfxOrderBase);
|
|
ASSERT(GfxOrderCeiling);
|
|
|
|
//
|
|
// validate that order is within range
|
|
//
|
|
if (pSysaudioGfx->ulOrder >= (GfxOrderCeiling - GfxOrderBase)) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
Trap();
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Allocate a Filter Node for the new GFX
|
|
//
|
|
pFilterNode = new FILTER_NODE(FILTER_TYPE_GFX);
|
|
if(pFilterNode == NULL) {
|
|
Trap();
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit;
|
|
}
|
|
|
|
pFilterNode->SetRenderCaptureFlags(pSysaudioGfx->ulType);
|
|
|
|
//
|
|
// Copy the Device Name (on which the gfx needs to be attached) into a local
|
|
// copy for our own use
|
|
//
|
|
Status = SafeCopyDeviceName(
|
|
(PWSTR) ((CHAR *) pSysaudioGfx + pSysaudioGfx->ulDeviceNameOffset),
|
|
cbMaxLength,
|
|
&pwstrDeviceName);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto exit;
|
|
}
|
|
|
|
DPF1(GFX_VERBOSE_LEVEL, " On DI = %s", DbgUnicode2Sz(pwstrDeviceName));
|
|
|
|
//
|
|
// Make sure that there are no other GFXes with the same order on this device
|
|
//
|
|
if ((FindGfx(pFilterNode,
|
|
0, // wild card for handle
|
|
pwstrDeviceName,
|
|
pSysaudioGfx->ulOrder+GfxOrderBase))) {
|
|
delete [] pwstrDeviceName;
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Get the FileObject of the GFX for future use
|
|
// SECURITY NOTE:
|
|
// The handle is coming from UserMode. So we have to specify UserMode.
|
|
// Also we are explicitly interested in FileObjects. The rest should be
|
|
// rejected.
|
|
//
|
|
Status = ObReferenceObjectByHandle(
|
|
pSysaudioGfx->hGfx,
|
|
FILE_GENERIC_READ | FILE_GENERIC_WRITE,
|
|
*IoFileObjectType,
|
|
UserMode,
|
|
(PVOID*)&pFileObject,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(Status) || NULL == pFileObject) {
|
|
DPF1(GFX_VERBOSE_LEVEL, "AddGfx :: ObReference failed %x", Status);
|
|
delete [] pwstrDeviceName;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Add the device name string to global memory to be freed
|
|
//
|
|
Status = pFilterNode->lstFreeMem.AddList(pwstrDeviceName);
|
|
if(!NT_SUCCESS(Status)) {
|
|
Trap();
|
|
delete [] pwstrDeviceName;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Indicate that this Gfx needs be loaded only on the device pointed to be
|
|
// pwstrDeviceName
|
|
//
|
|
Status = pFilterNode->AddDeviceInterfaceMatch(pwstrDeviceName);
|
|
if(!NT_SUCCESS(Status)) {
|
|
Trap();
|
|
delete [] pwstrDeviceName;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Set the Gfx order in the filter node
|
|
//
|
|
pFilterNode->SetOrder(pSysaudioGfx->ulOrder+GfxOrderBase);
|
|
|
|
//
|
|
// Profile the GFX and create pin infos, logical filter nodes etc
|
|
//
|
|
Status = pFilterNode->ProfileFilter(pFileObject);
|
|
if(!NT_SUCCESS(Status)) {
|
|
Trap();
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Fix the GFX glitching problem. Send the property blindly to GFX
|
|
// filter. KS will handle the property.
|
|
// Failures are not important, ignore them.
|
|
//
|
|
SetKsFrameHolding(pFileObject);
|
|
|
|
exit:
|
|
if(!NT_SUCCESS(Status)) {
|
|
DPF1(GFX_VERBOSE_LEVEL, "AddGfx :: Failed, Status = %x", Status);
|
|
if(pFilterNode != NULL) {
|
|
delete pFilterNode;
|
|
pFilterNode = NULL;
|
|
}
|
|
if(pFileObject != NULL) {
|
|
ObDereferenceObject(pFileObject);
|
|
}
|
|
}
|
|
else {
|
|
|
|
DPF1(GFX_VERBOSE_LEVEL, "AddGfx :: Added GFX FilterNode %x", pFilterNode);
|
|
DPF1(GFX_VERBOSE_LEVEL, " order = %x", pFilterNode->GetOrder());
|
|
DPF1(GFX_VERBOSE_LEVEL, " type = %x", pFilterNode->GetType());
|
|
DPF1(GFX_VERBOSE_LEVEL, " flags = %x", pFilterNode->GetFlags());
|
|
|
|
//
|
|
// Setup file handle details for later use of
|
|
// the user mode handle passed in
|
|
//
|
|
pFilterNode->SetFileDetails(pSysaudioGfx->hGfx,
|
|
pFileObject,
|
|
PsGetCurrentProcess());
|
|
//
|
|
// Force a rebuild of graph nodes
|
|
//
|
|
DestroyAllGraphs();
|
|
}
|
|
return(Status);
|
|
}
|
|
|
|
//=============================================================================
|
|
// Assumptions:
|
|
// - SysaudioGfx.ulType has been already validated.
|
|
//
|
|
NTSTATUS RemoveGfx(
|
|
PSYSAUDIO_GFX pSysaudioGfx,
|
|
ULONG cbMaxLength
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PFILTER_NODE pFilterNode;
|
|
PWSTR pwstrDeviceName;
|
|
ULONG GfxOrderBase, GfxOrderCeiling;
|
|
|
|
pFilterNode = NULL;
|
|
GfxOrderBase = GfxOrderCeiling = 0;
|
|
pwstrDeviceName = NULL;
|
|
|
|
DPF1(GFX_VERBOSE_LEVEL, "RemoveGfx :: Request to remove Gfx %x", pSysaudioGfx);
|
|
DPF1(GFX_VERBOSE_LEVEL, " hGfx = %x", pSysaudioGfx->hGfx);
|
|
DPF1(GFX_VERBOSE_LEVEL, " ulOrder = %x", pSysaudioGfx->ulOrder);
|
|
DPF1(GFX_VERBOSE_LEVEL, " ulType = %x", pSysaudioGfx->ulType);
|
|
DPF1(GFX_VERBOSE_LEVEL, " Flags = %x", pSysaudioGfx->ulFlags);
|
|
|
|
//
|
|
// Setup GFX Order's base & ceiling for future usage
|
|
//
|
|
if (pSysaudioGfx->ulType == GFX_DEVICETYPE_RENDER) {
|
|
GfxOrderBase = ORDER_RENDER_GFX_FIRST;
|
|
GfxOrderCeiling = ORDER_RENDER_GFX_LAST;
|
|
}
|
|
|
|
if (pSysaudioGfx->ulType == GFX_DEVICETYPE_CAPTURE ) {
|
|
GfxOrderBase = ORDER_CAPTURE_GFX_FIRST;
|
|
GfxOrderCeiling = ORDER_CAPTURE_GFX_LAST;
|
|
}
|
|
|
|
ASSERT(GfxOrderBase);
|
|
ASSERT(GfxOrderCeiling);
|
|
|
|
//
|
|
// Copy the Device Name (on which the gfx needs to be attached) into a local copy for our own use
|
|
//
|
|
Status = SafeCopyDeviceName(
|
|
(PWSTR) ((CHAR *) pSysaudioGfx + pSysaudioGfx->ulDeviceNameOffset),
|
|
cbMaxLength,
|
|
&pwstrDeviceName);
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto exit;
|
|
}
|
|
|
|
DPF1(GFX_VERBOSE_LEVEL, " On DI = %s", DbgUnicode2Sz(pwstrDeviceName));
|
|
|
|
//
|
|
// Find the FilterNode for the Gfx
|
|
//
|
|
if ((pFilterNode = FindGfx(NULL,
|
|
pSysaudioGfx->hGfx,
|
|
pwstrDeviceName,
|
|
pSysaudioGfx->ulOrder+GfxOrderBase)) == NULL) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Should we validate the FileHandle Value?
|
|
//
|
|
|
|
//
|
|
// Dereference the file object
|
|
//
|
|
pFilterNode->ClearFileDetails();
|
|
exit:
|
|
if(!NT_SUCCESS(Status)) {
|
|
DPF1(GFX_VERBOSE_LEVEL, "RemoveGfx :: Failed, Status = %x", Status);
|
|
Trap();
|
|
}
|
|
else {
|
|
delete pFilterNode;
|
|
}
|
|
delete pwstrDeviceName;
|
|
return(Status);
|
|
}
|
|
|
|
PFILTER_NODE
|
|
FindGfx(
|
|
PFILTER_NODE pnewFilterNode,
|
|
HANDLE hGfx,
|
|
PWSTR pwstrDeviceName,
|
|
ULONG GfxOrder
|
|
)
|
|
{
|
|
PFILTER_NODE pFilterNode;
|
|
ULONG DeviceCount;
|
|
UNICODE_STRING usInDevice, usfnDevice;
|
|
PWSTR pwstr;
|
|
|
|
DPF2(90, "FindGfx:: Looking for GFX with order = %x attached to %s)", GfxOrder, DbgUnicode2Sz(pwstrDeviceName));
|
|
|
|
FOR_EACH_LIST_ITEM(gplstFilterNode, pFilterNode) {
|
|
|
|
//
|
|
// Skip the one we just added
|
|
//
|
|
if (pFilterNode == pnewFilterNode) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check whether this pFilterNode matches the Gfx we are looking for
|
|
//
|
|
if (pFilterNode->DoesGfxMatch(hGfx, pwstrDeviceName, GfxOrder)) {
|
|
return (pFilterNode);
|
|
}
|
|
|
|
} END_EACH_LIST_ITEM
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
|
|
//=============================================================================
|
|
//
|
|
// Copies a UNICODE DeviceName to a new location. The source string is coming
|
|
// from user mode.
|
|
// There are assumptions in the code that the size should be greater than 4
|
|
// characters. (see DEVICE_NAME_TAG)
|
|
// Caller must make sure that this is BUFFERRED IO.
|
|
//
|
|
NTSTATUS
|
|
SafeCopyDeviceName(
|
|
PWSTR pwstrDeviceName,
|
|
ULONG cbMaxLength,
|
|
PWSTR *String
|
|
)
|
|
{
|
|
NTSTATUS ntStatus;
|
|
ULONG cchLength;
|
|
PWSTR pwstrString = NULL;
|
|
|
|
*String = NULL;
|
|
|
|
//
|
|
// SECURITY_NOTE:
|
|
// pwstrDeviceName points to a NULL-terminated UNICODE string.
|
|
// The string is coming from user mode, through BUFFERRED IO. So try/
|
|
// except is not necessary. Also Probe would not catch any errors.
|
|
// The IRP OutputBufferLength limits, the size of the string.
|
|
//
|
|
if (S_OK !=
|
|
StringCchLength(pwstrDeviceName, (size_t) cbMaxLength / sizeof(WCHAR), (size_t *) &cchLength))
|
|
{
|
|
DPF(5, "SafeCopyDeviceName: DeviceName is not zero-terminated.");
|
|
ntStatus = STATUS_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// SECURITY NOTE:
|
|
// There are assumptions further in the code about DeviceName string.
|
|
// Make sure those assumptions hold.
|
|
// One assumption is that DeviceName should be greater than 4
|
|
// characters.
|
|
//
|
|
if (cchLength <= wcslen(DEVICE_NAME_TAG)) {
|
|
DPF(5, "SafeCopyDeviceName: DeviceName is not well-formed");
|
|
ntStatus = STATUS_INVALID_PARAMETER;
|
|
goto exit;
|
|
}
|
|
|
|
pwstrString = new(WCHAR[cchLength + 1]) ;
|
|
if(pwstrString == NULL) {
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// SECURITY NOTE:
|
|
// Note that Length does not include the terminating NULL.
|
|
// Use n version of string copy in case the buffer changes.
|
|
// Also make sure that the string is NULL terminated.
|
|
//
|
|
wcsncpy(pwstrString, pwstrDeviceName, cchLength);
|
|
pwstrString[cchLength] = UNICODE_NULL;
|
|
ntStatus = STATUS_SUCCESS;
|
|
|
|
exit:
|
|
*String = pwstrString;
|
|
return ntStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
GetFilterTypeFromGuid(
|
|
IN LPGUID pguid,
|
|
OUT PULONG pfulType
|
|
)
|
|
{
|
|
int i;
|
|
for(i = 0; i < SIZEOF_ARRAY(apguidCategories); i++) {
|
|
if (memcmp (apguidCategories[i], pguid, sizeof(GUID)) == 0) {
|
|
*pfulType |= aulFilterType[i];
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
}
|
|
return(STATUS_INVALID_DEVICE_REQUEST);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
// End of File: notify.cpp
|
|
//---------------------------------------------------------------------------
|