//---------------------------------------------------------------------------
//
//  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"


//---------------------------------------------------------------------------
//---------------------------------------------------------------------------

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,
#ifdef KSCATEGORY_DRM_DESCRAMBLE
    &KSCATEGORY_DRM_DESCRAMBLE,
#endif
#ifdef KSCATEGORY_MICROPHONE_ARRAY_PROCESSOR
    &KSCATEGORY_MICROPHONE_ARRAY_PROCESSOR,
#endif
};

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,
#ifdef KSCATEGORY_DRM_DESCRAMBLE
    FILTER_TYPE_DRM_DESCRAMBLE,
#endif
#ifdef KSCATEGORY_MICROPHONE_ARRAY_PROCESSOR
    FILTER_TYPE_MIC_ARRAY_PROCESSOR,
#endif
};

PVOID pNotificationHandle = NULL;
PVOID pNotificationHandle2 = 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;
    }
    //
    // For compatibility with Intel AEC which isn't registered in the AUDIO
    // category, sysaudio needs to hook the AEC category.
    //
    Status = IoRegisterPlugPlayNotification(
      EventCategoryDeviceInterfaceChange,
      PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
      (LPGUID)&KSCATEGORY_ACOUSTIC_ECHO_CANCEL,
      gpDeviceInstance->pPhysicalDeviceObject->DriverObject,
      (NTSTATUS (*)(PVOID, PVOID))AudioDeviceInterfaceNotification,
      NULL,
      &pNotificationHandle2);

    if(!NT_SUCCESS(Status)) {
        Trap();
        goto exit;
    }
exit:
    return(Status);
}

VOID
UnregisterForPlugPlayNotifications(
)
{
    if(pNotificationHandle != NULL) {
        IoUnregisterPlugPlayNotification(pNotificationHandle);
    }
    if(pNotificationHandle2 != NULL) {
        IoUnregisterPlugPlayNotification(pNotificationHandle2);
    }
}

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 FDO.
    ObDereferenceObject(gpDeviceInstance->pFunctionalDeviceObject);

    return(STATUS_SUCCESS);
}

NTSTATUS
DeleteFilterWorker(
    PWSTR pwstrDeviceInterface,
    PVOID pReference
)
{
    DeleteFilter(pwstrDeviceInterface);
    ExFreePool(pwstrDeviceInterface);
    DecrementAddRemoveCount();

    // Dereference sysaudio FDO.
    ObDereferenceObject(gpDeviceInstance->pFunctionalDeviceObject);
    
    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),
            'ASYS');

    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 PnP does not REMOVE the device
        // when the Worker thread is running.
        // If the thread is scheduled successfully, it will remove the reference
        // when exiting.
        //
        ObReferenceObject(gpDeviceInstance->pFunctionalDeviceObject);

        InterlockedIncrement(&glPendingAddDelete);
        Status = QueueWorkList(
          (UTIL_PFN)AddFilterWorker,
          pwstrDeviceInterface,
          NULL);
        if (!NT_SUCCESS(Status)) {
            ObDereferenceObject(gpDeviceInstance->pFunctionalDeviceObject);
        }
    }
    else if(IsEqualGUID(&pNotification->Event, &GUID_DEVICE_INTERFACE_REMOVAL)) {
        //
        // Keep a reference so that PnP does not REMOVE the device
        // when the Worker thread is running.
        // If the thread is scheduled successfully, it will remove the reference
        // when exiting.
        //
        ObReferenceObject(gpDeviceInstance->pFunctionalDeviceObject);

        InterlockedIncrement(&glPendingAddDelete);
        Status = QueueWorkList(
          (UTIL_PFN)DeleteFilterWorker,
          pwstrDeviceInterface,
          NULL);
        if (!NT_SUCCESS(Status)) {
            ObDereferenceObject(gpDeviceInstance->pFunctionalDeviceObject);
        }
    }
    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.
        //
        ExFreePool(pwstrDeviceInterface);
    }

exit:
    if (!NT_SUCCESS(Status))
    {
        ExFreePool(pwstrDeviceInterface);
    }
    
    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

NTSTATUS AddGfx(
    PSYSAUDIO_GFX pSysaudioGfx
)
{
    NTSTATUS Status;
    PFILE_OBJECT pFileObject;
    PFILTER_NODE pFilterNode;
    ULONG Flags;
    PWSTR pwstrDeviceName;
    ULONG Length;
    ULONG GfxOrderBase, GfxOrderCeiling;

    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);

    //
    // validate type to be Capture or Render (use public include file!!!)
    //
    if ((pSysaudioGfx->ulType != GFX_DEVICETYPE_RENDER) && (pSysaudioGfx->ulType != GFX_DEVICETYPE_CAPTURE)) { 
        Trap();
        Status = STATUS_INVALID_PARAMETER;
        goto exit;
    }

    //
    // 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 = SafeCopyStringFromOffset(pSysaudioGfx, pSysaudioGfx->ulDeviceNameOffset, &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
    //
    Status = ObReferenceObjectByHandle(
      pSysaudioGfx->hGfx,
      GENERIC_READ | GENERIC_WRITE,
      NULL,
      KernelMode,
      (PVOID*)&pFileObject,
      NULL);

    if (!NT_SUCCESS(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();
        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);
}

NTSTATUS RemoveGfx(
    PSYSAUDIO_GFX pSysaudioGfx
)
{
    NTSTATUS Status;
    PFILE_OBJECT pFileObject=NULL;
    PFILTER_NODE pFilterNode;
    ULONG Flags;
    PWSTR pwstrDeviceName;
    ULONG Length;
    ULONG GfxOrderBase, GfxOrderCeiling;

    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);

    //
    // validate type to be Capture or Render (use public include file!!!)
    //
    if ((pSysaudioGfx->ulType != GFX_DEVICETYPE_RENDER) && (pSysaudioGfx->ulType != GFX_DEVICETYPE_CAPTURE)) {
        Trap();
        Status = STATUS_INVALID_PARAMETER;
        goto exit;
    }

    //
    // 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 = SafeCopyStringFromOffset(pSysaudioGfx, pSysaudioGfx->ulDeviceNameOffset, &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
    //
    Status = 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);
}

NTSTATUS
SafeCopyStringFromOffset(
    PVOID pBasePointer,
    ULONG Offset,
    PWSTR *String
)
{
    ULONG Length;
    PWSTR pwstrString = NULL;

    *String = NULL;

    __try {
        Length = wcslen( (PWSTR)(((CHAR *)pBasePointer)+Offset)) + 1;
        pwstrString = new(WCHAR[Length]) ;

        if(pwstrString == NULL) {
            return(STATUS_INSUFFICIENT_RESOURCES);
        }
        wcscpy(pwstrString,(PWSTR)(((CHAR *)pBasePointer)+Offset));
    } __except (EXCEPTION_EXECUTE_HANDLER) {
        Trap();
        delete [] pwstrString;
        return(STATUS_INVALID_PARAMETER);
    }
    *String = pwstrString;
    return(STATUS_SUCCESS);
}

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
//---------------------------------------------------------------------------