//==========================================================================;
//
//  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) 1992 - 1998  Microsoft Corporation.  All Rights Reserved.
//
//==========================================================================;


#include <wdm.h>
#include <limits.h>
#include <unknown.h>
#include <ks.h>
#include <ksmedia.h>
#include <bdatypes.h>
#include <bdamedia.h>
#include <bdasup.h>
#include "bdasupi.h"

/*
 -  DriverEntry
 -
 *  This the the required DriverEntry for the BDA Support Driver.
 *  Though required, it is never actually called.
 *
 */
NTSTATUS
DriverEntry (
    IN PDRIVER_OBJECT    pDriverObject,
    IN PUNICODE_STRING   pszuRegistryPath
    )
//////////////////////////////////////////////////////////////////////////////////////
{
//$BUGBUG   This entry point is required but never called.

    return STATUS_SUCCESS;
}


STDMETHODIMP_(NTSTATUS)
BdaFindContextEntry(
    PBDA_CONTEXT_LIST   pContextList,
    PVOID               pvReference,
    PVOID *             ppvContext
    )
{
    NTSTATUS            status = STATUS_SUCCESS;
    ULONG               uliEntry;
    KIRQL               oldIrql;

    ASSERT( pContextList);
    ASSERT( ppvContext);

    if (!pContextList->fInitialized)
    {
        status = STATUS_NOT_FOUND;
        goto errExit;
    }

    //  NULL pvReference is not valid.
    //
    if (!pvReference)
    {
        status = STATUS_INVALID_PARAMETER;
        *ppvContext = NULL;
        goto errExit;
    }

    //  Lock down the list while we search it.
    //
    KeAcquireSpinLock( &(pContextList->lock), &oldIrql);

    //  Find a list entry with a matching pvReference
    //
    for (uliEntry = 0; uliEntry < pContextList->ulcListEntries; uliEntry++)
    {
        if (pContextList->pListEntries[uliEntry].pvReference == pvReference)
        {
            break;
        }
    }

    if (uliEntry >= pContextList->ulcListEntries)
    {
        //  No matching entry was found so return error.
        //
        status = STATUS_NOT_FOUND;
        *ppvContext = NULL;
    }
    else
    {
        //  Return the pvContext corresponding to the matching pvReference.
        //
        *ppvContext = pContextList->pListEntries[uliEntry].pvContext;
    }

    KeReleaseSpinLock( &(pContextList->lock), oldIrql);

errExit:
    return status;
}


STDMETHODIMP_(NTSTATUS)
BdaCreateContextEntry(
    PBDA_CONTEXT_LIST   pContextList,
    PVOID               pvReference,
    ULONG               ulcbContext,
    PVOID *             ppvContext
    )
{
    NTSTATUS            status = STATUS_SUCCESS;
    ULONG               uliEntry;
    KIRQL               oldIrql;

    ASSERT( pContextList);
    ASSERT( ppvContext);

    if (!pContextList->fInitialized)
    {
        KeInitializeSpinLock ( &(pContextList->lock));
        pContextList->fInitialized = TRUE;
    }

    //  See if a list entry has already been created.
    //
    status = BdaFindContextEntry( pContextList, pvReference, ppvContext);
    if (status != STATUS_NOT_FOUND)
    {
        goto errExit;
    }
    status = STATUS_SUCCESS;

    KeAcquireSpinLock( &(pContextList->lock), &oldIrql);

    //  If the current block of context entries is full, allocate
    //  a bigger block to put the new entry into.
    //
    if (pContextList->ulcListEntries >= pContextList->ulcMaxListEntries)
    {
        ULONG               ulcEntriesToAllocate;
        PBDA_CONTEXT_ENTRY  pNewList;

        ulcEntriesToAllocate =  pContextList->ulcMaxListEntries
                              + pContextList->ulcListEntriesPerBlock;

        pNewList = (PBDA_CONTEXT_ENTRY) ExAllocatePool(
                       NonPagedPool,
                       ulcEntriesToAllocate * sizeof( BDA_CONTEXT_ENTRY)
                       );
        if (!pNewList)
        {
            status = STATUS_NO_MEMORY;
            KeReleaseSpinLock( &(pContextList->lock), oldIrql);
            goto errExit;
        }

        RtlZeroMemory( pNewList,
                        ulcEntriesToAllocate * sizeof( BDA_CONTEXT_ENTRY)
                     );
        if (pContextList->pListEntries)
        {
            RtlMoveMemory( pNewList,
                           pContextList->pListEntries,
                             pContextList->ulcMaxListEntries
                           * sizeof( BDA_CONTEXT_ENTRY)
                         );
            ExFreePool( pContextList->pListEntries);
        }

        pContextList->pListEntries = pNewList;
        pContextList->ulcMaxListEntries = ulcEntriesToAllocate;
    }

#ifdef SORTED_CONTEXT_ENTRIES

    //  Find the proper place to insert the new entry into the list.
    //
    for (uliEntry = 0; uliEntry < pContextList->ulcListEntries; uliEntry++)
    {
        if (pContextList->pListEntries[uliEntry].pvReference > pvReference)
        {
            break;
        }
    }

#else

    uliEntry = pContextList->ulcListEntries;

#endif // SORTED_CONTEXT_ENTRIES


    //  Allocate a new context entry
    //
    *ppvContext = ExAllocatePool( NonPagedPool, ulcbContext);
    if (!*ppvContext)
    {
        status = STATUS_NO_MEMORY;
        KeReleaseSpinLock( &(pContextList->lock), oldIrql);
        goto errExit;
    }

#ifdef SORTED_CONTEXT_ENTRIES

    //  If the new entry is in the middle of the list, then create
    //  a whole for it by moving the end of the list down.
    //
    if (uliEntry < pContextList->ulcListEntries)
    {
        //  NOTE!  RtlMoveMemory handles overlapped source and destination.
        //
        RtlMoveMemory( &(pContextList->pListEntries[uliEntry + 1]),
                       &(pContextList->pListEntries[uliEntry]),
                         (pContextList->ulcListEntries - uliEntry)
                       * sizeof( BDA_CONTEXT_ENTRY)
                     );
    }

#endif // SORTED_CONTEXT_ENTRIES


    RtlZeroMemory( *ppvContext, ulcbContext);
    pContextList->pListEntries[uliEntry].pvContext = *ppvContext;
    pContextList->pListEntries[uliEntry].ulcbContext = ulcbContext;
    pContextList->pListEntries[uliEntry].pvReference = pvReference;
    pContextList->ulcListEntries++;

    KeReleaseSpinLock( &(pContextList->lock), oldIrql);

errExit:
    return status;
}


STDMETHODIMP_(NTSTATUS)
BdaDeleteContextEntry(
    PBDA_CONTEXT_LIST   pContextList,
    PVOID               pvReference
    )
{
    NTSTATUS            status = STATUS_SUCCESS;
    ULONG               uliEntry;
    KIRQL               oldIrql;
    PVOID               pvContext;
    ULONG               ulcbContext;

    ASSERT( pContextList);
    ASSERT( pvReference);
    ASSERT( pContextList->fInitialized);

    if (!pContextList->fInitialized)
    {
        goto errExit;
    }

    KeAcquireSpinLock( &(pContextList->lock), &oldIrql);

    //  Find the Context Entry in the list
    //
    for (uliEntry = 0; uliEntry < pContextList->ulcListEntries; uliEntry++)
    {
        if (pContextList->pListEntries[uliEntry].pvReference == pvReference)
        {
            break;
        }
    }

    if (uliEntry >= pContextList->ulcListEntries)
    {
        status = STATUS_NOT_FOUND;
        KeReleaseSpinLock( &(pContextList->lock), oldIrql);
        goto errExit;
    }

    pvContext = pContextList->pListEntries[uliEntry].pvContext;
    ulcbContext = pContextList->pListEntries[uliEntry].ulcbContext;
    pContextList->pListEntries[uliEntry].pvContext = NULL;
    pContextList->pListEntries[uliEntry].pvReference = NULL;
    RtlZeroMemory( pvContext, ulcbContext);
    ExFreePool( pvContext);

    pContextList->ulcListEntries -= 1;
    if (uliEntry < pContextList->ulcListEntries)
    {
        //  NOTE!  RtlMoveMemory handles overlapped source and destination.
        //
        RtlMoveMemory( &(pContextList->pListEntries[uliEntry]),
                       &(pContextList->pListEntries[uliEntry + 1]),
                       (pContextList->ulcListEntries - uliEntry)
                       * sizeof( BDA_CONTEXT_ENTRY)
                     );
    }

    KeReleaseSpinLock( &(pContextList->lock), oldIrql);

errExit:
    return status;
}


STDMETHODIMP_(NTSTATUS)
BdaDeleteContextEntryByValue(
    PBDA_CONTEXT_LIST   pContextList,
    PVOID               pvContext
    )
{
    NTSTATUS            status = STATUS_SUCCESS;
    ULONG               uliEntry;
    KIRQL               oldIrql;
    ULONG               ulcbContext;

    ASSERT( pContextList);
    ASSERT( pvContext);
    ASSERT( pContextList->fInitialized);

    if (!pContextList->fInitialized)
    {
        goto errExit;
    }

    KeAcquireSpinLock( &(pContextList->lock), &oldIrql);

    //  Find the Context Entry in the list
    //
    for (uliEntry = 0; uliEntry < pContextList->ulcListEntries; uliEntry++)
    {
        if (pContextList->pListEntries[uliEntry].pvContext == pvContext)
        {
            break;
        }
    }

    if (uliEntry >= pContextList->ulcListEntries)
    {
        status = STATUS_NOT_FOUND;
        KeReleaseSpinLock( &(pContextList->lock), oldIrql);
        goto errExit;
    }

    ulcbContext = pContextList->pListEntries[uliEntry].ulcbContext;
    pContextList->pListEntries[uliEntry].pvContext = NULL;
    pContextList->pListEntries[uliEntry].pvReference = NULL;
    RtlZeroMemory( pvContext, ulcbContext);
    ExFreePool( pvContext);

    pContextList->ulcListEntries -= 1;
    if (uliEntry < pContextList->ulcListEntries)
    {
        //  NOTE!  RtlMoveMemory handles overlapped source and destination.
        //
        RtlMoveMemory( &(pContextList->pListEntries[uliEntry]),
                       &(pContextList->pListEntries[uliEntry + 1]),
                       (pContextList->ulcListEntries - uliEntry)
                       * sizeof( BDA_CONTEXT_ENTRY)
                     );
    }

    KeReleaseSpinLock( &(pContextList->lock), oldIrql);

errExit:
    return status;
}


/*
**  BdaDeleteFilterFactoryContextByValue()
**
**  Finds the given BDA Filter Factory Context in the FilterFactory
**  context list and removes it.
**
**  This function is provided as a callback when the Filter Facotry Context
**  is added to the KSFilterFactory's Object Bag.  This allows KS to clean
**  up the context when the filter factory is unexpectedly closed.
**
** Arguments:
**
**
** Returns:
**
**
**
** Side Effects:  none
*/

BDA_CONTEXT_LIST    FilterFactoryContextList = { 0, 0, 4, NULL, 0, FALSE};

STDMETHODIMP_(VOID)
BdaDeleteFilterFactoryContextByValue(
    PVOID       pFilterFactoryCtx
    )
{
    BdaDeleteContextEntryByValue( &FilterFactoryContextList,
                                  pFilterFactoryCtx
                                );
}


/*
**  BdaCreateFilterFactoryContext()
**
**  Finds or creates a BDA Filter Factory Context that corresponds
**  to the given KS Filter Factory.
**
** Arguments:
**
**
** Returns:
**
**
**
** Side Effects:  none
*/

STDMETHODIMP_(NTSTATUS)
BdaCreateFilterFactoryContext(
    PKSFILTERFACTORY                pKSFilterFactory,
    PBDA_FILTER_FACTORY_CONTEXT *   ppFilterFactoryCtx
    )
{
    NTSTATUS                    status = STATUS_SUCCESS;

    status = BdaCreateContextEntry( &FilterFactoryContextList,
                                    pKSFilterFactory,
                                    sizeof( BDA_FILTER_FACTORY_CONTEXT),
                                    (PVOID *) ppFilterFactoryCtx
                                  );
    if (!NT_SUCCESS( status))
    {
        goto errExit;
    }

    status = KsAddItemToObjectBag( pKSFilterFactory->Bag,
                                   *ppFilterFactoryCtx,
                                   BdaDeleteFilterFactoryContextByValue
                                 );

errExit:
    return status;
}


/*
**  BdaDestructFilterContext()
**
**  Finds the given BDA Filter Context in the Filter
**  context list and removes it.
**
**  This function is provided as a callback when the Filter Context is
**  added to the KSFilter's Object Bag.  This allows KS to clean up the
**  context when the filter is unexpectedly closed.
**
** Arguments:
**
**
** Returns:
**
**
**
** Side Effects:  none
*/


STDMETHODIMP_(VOID)
BdaDestructFilterContext(
    PBDA_FILTER_CONTEXT       pFilterCtx
    )
{
    ULONG       uliPath;

    ASSERT( pFilterCtx);
    
    if (!pFilterCtx || !pFilterCtx->argpPathInfo)
    {
        goto exit;
    }

    //  Delete the path information.
    //

    for ( uliPath = 0; uliPath < pFilterCtx->ulcPathInfo; uliPath++)
    {
        if (pFilterCtx->argpPathInfo[uliPath])
        {
            ExFreePool( pFilterCtx->argpPathInfo[uliPath]);
            pFilterCtx->argpPathInfo[uliPath] = NULL;
        }
    }

    ExFreePool( pFilterCtx->argpPathInfo);
    pFilterCtx->argpPathInfo = NULL;
    pFilterCtx->ulcPathInfo = 0;

exit:
    return;
}


/*
**  BdaDeleteFilterContextByValue()
**
**  Finds the given BDA Filter Context in the Filter
**  context list and removes it.
**
**  This function is provided as a callback when the Filter Context is
**  added to the KSFilter's Object Bag.  This allows KS to clean up the
**  context when the filter is unexpectedly closed.
**
** Arguments:
**
**
** Returns:
**
**
**
** Side Effects:  none
*/

BDA_CONTEXT_LIST    FilterContextList = { 0, 0, 4, NULL, 0, FALSE};

STDMETHODIMP_(VOID)
BdaDeleteFilterContextByValue(
    PVOID       pFilterCtx
    )
{
    BdaDestructFilterContext( (PBDA_FILTER_CONTEXT) pFilterCtx);

    BdaDeleteContextEntryByValue( &FilterContextList,
                                  pFilterCtx
                                );
}


/*
**  BdaCreateFilterContext()
**
**  Finds or creates a BDA Filter Context that corresponds
**  to the given KS Filter.
**
** Arguments:
**
**
** Returns:
**
**
**
** Side Effects:  none
*/

STDMETHODIMP_(NTSTATUS)
BdaCreateFilterContext(
    PKSFILTER               pKSFilter,
    PBDA_FILTER_CONTEXT *   ppFilterCtx
    )
{
    NTSTATUS                    status = STATUS_SUCCESS;

    status = BdaCreateContextEntry( &FilterContextList,
                                    pKSFilter,
                                    sizeof( BDA_FILTER_CONTEXT),
                                    (PVOID *) ppFilterCtx
                                  );
    if (!NT_SUCCESS( status))
    {
        goto errExit;
    }

    status = KsAddItemToObjectBag( pKSFilter->Bag,
                                   *ppFilterCtx,
                                   BdaDeleteFilterContextByValue
                                 );

    (*ppFilterCtx)->pKSFilter = pKSFilter;

errExit:
    return status;
}


/*
**  BdaGetFilterContext()
**
**  Finds a BDA Filter Context that corresponds
**  to the given KS Filter Instance.
**
** Arguments:
**
**
** Returns:
**
**
**
** Side Effects:  none
*/

STDMETHODIMP_(NTSTATUS)
BdaGetFilterContext(
    PKSFILTER                       pKSFilter,
    PBDA_FILTER_CONTEXT *           ppFilterCtx
    )
{
    NTSTATUS                    status = STATUS_SUCCESS;

    status = BdaFindContextEntry( &FilterContextList,
                                  pKSFilter,
                                  (PVOID *) ppFilterCtx
                                );

    return status;
}


/*
**  BdaDeleteFilterContext()
**
**  Deletes a BDA Filter Context.
**
** Arguments:
**
**
** Returns:
**
**
**
** Side Effects:  none
*/

STDMETHODIMP_(NTSTATUS)
BdaDeleteFilterContext(
    PVOID               pvReference
    )
{
    NTSTATUS                    status = STATUS_SUCCESS;
    ULONG                       uliPath;
    PBDA_FILTER_CONTEXT         pFilterCtx;

    status = BdaGetFilterContext( (PKSFILTER) pvReference, &pFilterCtx);
    if (status == STATUS_SUCCESS)
    {
        BdaDestructFilterContext( pFilterCtx);
    }

    status = BdaDeleteContextEntry( &FilterContextList,
                                    pvReference
                                  );
    return status;
}


/*
**  BdaGetControllingPinType()
**
**  
**
** Arguments:
**
**
** Returns:
**
**
**
** Side Effects:  none
*/

STDMETHODIMP_(NTSTATUS)
BdaGetControllingPinType( 
    ULONG                       ulNodeType,
    ULONG                       ulInputPinType,
    ULONG                       ulOutputPinType,
    PBDA_FILTER_CONTEXT         pFilterCtx,
    PULONG                      pulControllingPinType
    )
{
    NTSTATUS                    status = STATUS_NOT_FOUND;
    ULONG                       ulControllingPinType;
    ULONG                       uliPath;
    const KSFILTER_DESCRIPTOR * pKSFilterDescriptor;

    ASSERT( pFilterCtx);
    ASSERT( pFilterCtx->pBdaFilterTemplate);
    ASSERT( pFilterCtx->pBdaFilterTemplate->pFilterDescriptor);

    if (   !pFilterCtx
        || !pFilterCtx->pBdaFilterTemplate
        || !pFilterCtx->pBdaFilterTemplate->pFilterDescriptor
       )
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }

    if (   !pFilterCtx->ulcPathInfo
        || !pFilterCtx->argpPathInfo
        || !pFilterCtx->pBdaFilterTemplate->pFilterDescriptor->Connections
       )
    {
        goto errExit;
    }

    pKSFilterDescriptor = pFilterCtx->pBdaFilterTemplate->pFilterDescriptor;

    for (uliPath = 0; uliPath < pFilterCtx->ulcPathInfo; uliPath++)
    {
        PBDA_PATH_INFO      pPathInfo;
        ULONG               uliPathEntry;

        pPathInfo = pFilterCtx->argpPathInfo[uliPath];

        if (   !pPathInfo
            || (pPathInfo->ulInputPin != ulInputPinType)
            || (pPathInfo->ulOutputPin != ulOutputPinType)
           )
        {
            //  This is not the path for this pin pair.
            //
            continue;
        }

        //  Search the Path for the given node type.
        //
        ulControllingPinType = ulInputPinType;
        for ( uliPathEntry = 0
            ; uliPathEntry < pPathInfo->ulcPathEntries
            ; uliPathEntry++
            )
        {
            ULONG                           uliConnection;

            //  If we encounter topology joint then switch the controlling
            //  pin to be the output pin.
            //
            if (pPathInfo->rgPathEntries[uliPathEntry].fJoint)
            {
                ulControllingPinType = ulOutputPinType;
            }

            uliConnection = pPathInfo->rgPathEntries[uliPathEntry].uliConnection;
            if (pKSFilterDescriptor->Connections[uliConnection].ToNode == ulNodeType)
            {
                //  We found the controlling pin type for the node type.
                //  Indicate success and set the output parameter.
                //
                status = STATUS_SUCCESS;
                *pulControllingPinType = ulControllingPinType;
                break;
            }
        }

        if (uliPathEntry < pPathInfo->ulcPathEntries)
        {
            //  We found the controlling pin type for the node type.
            //
            break;
        }
    }
    
errExit:
    return status;
}


/*
**  BdaFilterInitTopologyData()
**
**  Initializes the common BDA filter context's topology info.
**
** Arguments:
**
**
** Returns:
**
**
**
** Side Effects:  none
*/

STDMETHODIMP_(NTSTATUS)
BdaFilterInitTopologyData(
    PBDA_FILTER_CONTEXT     pFilterCtx   
    )
{
    NTSTATUS                    status = STATUS_SUCCESS;
    ULONG                       ulcTemplateNodes;
    PBDA_NODE_CONTROL_INFO      pNodeControlInfo = NULL;

    ASSERT( pFilterCtx);
    ASSERT( pFilterCtx->pKSFilter);
    ASSERT( pFilterCtx->pBdaFilterTemplate);
    ASSERT( pFilterCtx->pBdaFilterTemplate->pFilterDescriptor);

    if (   !pFilterCtx
        || !pFilterCtx->pBdaFilterTemplate
        || !pFilterCtx->pBdaFilterTemplate->pFilterDescriptor
        || !pFilterCtx->pKSFilter
       )
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }

#ifdef REMOVE

    ulcTemplateNodes 
        = pFilterCtx->pBdaFilterTemplate->pFilterDescriptor->NodeDescriptorsCount;

    if (ulcTemplateNodes)
    {
        PKSNODE_DESCRIPTOR  pCurNode;
        ULONG               uliNode;

        ASSERT( pFilterCtx->pBdaFilterTemplate->pFilterDescriptor->NodeDescriptors);
        ASSERT( pFilterCtx->pBdaFilterTemplate->pFilterDescriptor->NodeDescriptorSize);

        //  Allocate an array of node control info structures
        //
        pNodeControlInfo = ExAllocatePool( 
                               NonPagedPool, 
                               ulcTemplateNodes * sizeof( BDA_NODE_CONTROL_INFO)
                               );
        if (!pNodeControlInfo)
        {
            status = STATUS_NO_MEMORY;
            goto errExit;
        }
        RtlZeroMemory( pNodeControlInfo,
                       ulcTemplateNodes * sizeof( BDA_NODE_CONTROL_INFO)
                       );

        //  Add the allocation to the KS Filter's object bag so that it
        //  will be freed on filter destruction.
        //
        status = KsAddItemToObjectBag( pFilterCtx->pKSFilter->Bag,
                                       pNodeControlInfo,
                                       NULL
                                       );

        //  Point the BDA Filter Context at the node control info.
        //
        pFilterCtx->argNodeControlInfo = pNodeControlInfo;
    
        //  Determine the contolling pin type for each node type and fill
        //  it in to the node control array
        //
        for ( uliNode = 0
            ; uliNode < ulcTemplateNodes
            ; uliNode++, pNodeControlInfo++
            )
        {
            //  BdaSup.sys always uses the index of the node descriptor as
            //  the node type.
            //
            pNodeControlInfo->ulNodeType = uliNode;

            //  Determine which template pin type controls this node type.
            //
            status = BdaGetControllingPinType( 
                         uliNode, 
                         pFilterCtx->pBdaFilterTemplate,
                         &pNodeControlInfo->ulControllingPinType
                         );
            if (status != STATUS_SUCCESS)
            {
                goto errExit;
            }
            
            //  Add the node control info as we determine it.
            //
            pFilterCtx->ulcNodeControlInfo++;
        }
    }
#endif // REMOVE

errExit:
    return status;
}


/*
**  BdaAddPinFactoryContext()
**
**  Adds pin factory information to the array of pin factory context
**  structures for this filter instance.  It will enlarge the array
**  if necessary.
**  NOTE!  Since the array is an array of structure NOT pointers to
**  structures, AND since the array can be moved, one should NOT keep
**  pointers to the pin factory context entries.
**
** Arguments:
**
**
** Returns:
**
**
**
** Side Effects:  none
*/

STDMETHODIMP_(NTSTATUS)
BdaCreatePinFactoryContext(
    PKSFILTER                       pKSFilter,
    PBDA_FILTER_CONTEXT             pFilterCtx,
    ULONG                           uliPinId,
    ULONG                           ulPinType
    )
{
    NTSTATUS                    status = STATUS_SUCCESS;


    //  Add the Pin Factory info to the filter context.
    //
    if (uliPinId >= pFilterCtx->ulcPinFactoriesMax)
    {
        //  If there isn't enough room then add more.
        //
        PBDA_PIN_FACTORY_CONTEXT    argNewPinCtx = NULL;
        PVOID                       pvTemp;
        ULONG                       ulcPinFactoriesMax;

        ulcPinFactoriesMax = uliPinId + BDA_PIN_STORAGE_INCREMENT;

        argNewPinCtx = ExAllocatePool( 
                               NonPagedPool,
                               ulcPinFactoriesMax * sizeof(BDA_PIN_FACTORY_CONTEXT)
                               );
        if (!argNewPinCtx)
        {
            status = STATUS_NO_MEMORY;
            goto errExit;
        }
        
        if (pFilterCtx->argPinFactoryCtx)
        {
            RtlMoveMemory( argNewPinCtx,
                           pFilterCtx->argPinFactoryCtx,
                           pFilterCtx->ulcPinFactoriesMax * sizeof(BDA_PIN_FACTORY_CONTEXT)
                           );
        }

        KsAddItemToObjectBag( pKSFilter->Bag,
                              argNewPinCtx,
                              NULL
                              );

        pvTemp = pFilterCtx->argPinFactoryCtx;
        pFilterCtx->argPinFactoryCtx = argNewPinCtx;
        pFilterCtx->ulcPinFactoriesMax = ulcPinFactoriesMax;

        KsRemoveItemFromObjectBag( pKSFilter->Bag,
                                   pvTemp,
                                   TRUE
                                   );
    }

    //  Fill in the pin factory context information.
    //
    pFilterCtx->argPinFactoryCtx[uliPinId].ulPinType = ulPinType;
    pFilterCtx->argPinFactoryCtx[uliPinId].ulPinFactoryId = uliPinId;
    if (uliPinId >= pFilterCtx->ulcPinFactories)
    {
        pFilterCtx->ulcPinFactories = uliPinId + 1;

    }

errExit:
    return status;
}


/*
**  BdaInitFilter()
**
**  Creates a BDA filter context for use by BdaCreatePinFactory etc.
**
** Arguments:
**
**
** Returns:
**
**
**
** Side Effects:  none
*/

STDMETHODIMP_(NTSTATUS)
BdaInitFilter(
    PKSFILTER                       pKSFilter,
    const BDA_FILTER_TEMPLATE *     pBdaFilterTemplate
    )
{
    NTSTATUS                    status = STATUS_SUCCESS;
    PBDA_FILTER_FACTORY_CONTEXT pFilterFactoryCtx = NULL;
    PBDA_FILTER_CONTEXT         pFilterCtx = NULL;
    PKSFILTERFACTORY            pKSFilterFactory = NULL;
    ULONG                       ulcPinFactoriesMax;
    const KSFILTER_DESCRIPTOR * pInitialFilterDescriptor = NULL;

    status = BdaFindContextEntry( &FilterContextList,
                                  pKSFilter,
                                  (PVOID *) &pFilterCtx
                                );
    if (NT_SUCCESS( status))
    {
        status = STATUS_SHARING_VIOLATION;
        goto errExit;
    }
    if (status != STATUS_NOT_FOUND)
    {
        goto errExit;
    }


    //  Get the filter factory context so that we can determine
    //  the initial pin list.
    //
    pKSFilterFactory = KsFilterGetParentFilterFactory( pKSFilter);

    ASSERT( pKSFilterFactory);

    if (!pKSFilterFactory)
    {
        status = STATUS_INVALID_DEVICE_STATE;
        goto errExit;
    }

    status = BdaFindContextEntry( &FilterFactoryContextList,
                                  pKSFilterFactory,
                                  (PVOID *) &pFilterFactoryCtx
                                );
    if (!NT_SUCCESS( status))
    {
        goto errExit;
    }

    if (!pFilterFactoryCtx)
    {
        status = STATUS_INVALID_DEVICE_STATE;
        goto errExit;
    }

    pInitialFilterDescriptor = pFilterFactoryCtx->pInitialFilterDescriptor;

    //  Create a BDA filter context and put it in the list so we can
    //  find it when BDA calls are made relative to the filter.
    //
    status = BdaCreateFilterContext( pKSFilter, &pFilterCtx);
    if (status != STATUS_SUCCESS)
    {
        goto errExit;
    }

    //  Point the BDA filter context at the template topology for the
    //  filter.
    //
    if (pBdaFilterTemplate)
    {
        pFilterCtx->pBdaFilterTemplate = pBdaFilterTemplate;
    }
    else
    {
        pFilterCtx->pBdaFilterTemplate
            = pFilterFactoryCtx->pBdaFilterTemplate;
    }
    
    //  Expand the template topology information into a list
    //  of paths keyed by the input-output pin type pair.
    //
    status = BdaCreateTemplatePaths( pFilterCtx->pBdaFilterTemplate,
                                     &pFilterCtx->ulcPathInfo,
                                     &pFilterCtx->argpPathInfo
                                     );
    if (!NT_SUCCESS( status))
    {
        goto errExit;
    }

    //$REVIEW - Should we allow filters with no input-output paths?
    //
    ASSERT( pFilterCtx->ulcPathInfo);
    ASSERT( pFilterCtx->argpPathInfo);

    //  Allocate space for the Pin Factory context information
    //
    ulcPinFactoriesMax = pBdaFilterTemplate->pFilterDescriptor->PinDescriptorsCount;
    ulcPinFactoriesMax += BDA_PIN_STORAGE_INCREMENT;
    pFilterCtx->argPinFactoryCtx 
        = ExAllocatePool( NonPagedPool,
                          ulcPinFactoriesMax * sizeof( BDA_PIN_FACTORY_CONTEXT)
                          );
    if (!pFilterCtx->argPinFactoryCtx)
    {
        status = STATUS_NO_MEMORY;
        goto errExit;
    }
    pFilterCtx->ulcPinFactories = 0;
    pFilterCtx->ulcPinFactoriesMax = ulcPinFactoriesMax;


    //  Loop through each initial pin descriptor and fill in the pin
    //  context info.
    //
    if (pInitialFilterDescriptor && pInitialFilterDescriptor->PinDescriptors)
    {
        ULONG   ulcbPinDescriptor;
        ULONG   uliPinType;

        if (pInitialFilterDescriptor->PinDescriptorsCount > pFilterCtx->ulcPinFactoriesMax)
        {
            status = STATUS_INVALID_DEVICE_STATE;
            goto errExit;
        }

        ulcbPinDescriptor = pInitialFilterDescriptor->PinDescriptorSize;
        for ( uliPinType = 0
            ; uliPinType < pInitialFilterDescriptor->PinDescriptorsCount
            ; uliPinType++
            )
        {
            ULONG   ulPinId;

            //  It is a BDA requirement that the index of all pins listed in the initial
            //  filter descriptor correspond to the index of its pin type
            //  in the BDA Template Descriptor.
            //

            status = BdaCreatePin( pKSFilter,
                                   uliPinType,
                                   &ulPinId
                                   );
            if (status != STATUS_SUCCESS)
            {
                goto errExit;
            }

            //
            //  We do not "CreateTopology" on the initial pins.  The
            //  initial pins are usually only input pins.  The Network
            //  Provider will create output pins and "CreateTopology".
            //
        }
    }


errExit:
    return status;
}


/*
**  BdaUninitFilter()
**
**  Deletes the BDA filter context for use by BdaCreatePinFactory etc.
**
** Arguments:
**
**
** Returns:
**
**
**
** Side Effects:  none
*/

STDMETHODIMP_(NTSTATUS)
BdaUninitFilter(
    PKSFILTER                       pKSFilter
    )
{
    NTSTATUS                    status = STATUS_SUCCESS;
#ifdef NO_KS_OBJECT_BAG
    status = BdaDeleteContextEntry( &FilterContextList,
                                    pKSFilter
                                );
    if (!NT_SUCCESS( status))
    {
        goto errExit;
    }

errExit:
#endif // def NO_KS_OBJECT_BAG
    return status;
}


/*
**  BdaCreateFilterFactoryEx()
**
**  Initializes the common BDA filter context.
**
** Arguments:
**
**
** Returns:
**
**
**
** Side Effects:  none
*/

STDMETHODIMP_(NTSTATUS)
BdaCreateFilterFactoryEx(
    PKSDEVICE                       pKSDevice,
    const KSFILTER_DESCRIPTOR *     pInitialFilterDescriptor,
    const BDA_FILTER_TEMPLATE *     pBdaFilterTemplate,
    PKSFILTERFACTORY *              ppKSFilterFactory
    )
{
    NTSTATUS                    status = STATUS_SUCCESS;
    PBDA_FILTER_FACTORY_CONTEXT pFilterFactoryCtx = NULL;
    PKSFILTERFACTORY            pKSFilterFactory = NULL;
    PKSFILTER_DESCRIPTOR        pFilterDescriptor = NULL;
    PKSAUTOMATION_TABLE         pNewAutomationTable = NULL;
    
    ASSERT( pKSDevice);
    if (!pKSDevice)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }
    ASSERT( pInitialFilterDescriptor);
    if (!pInitialFilterDescriptor)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }
    ASSERT( pBdaFilterTemplate);
    if (!pBdaFilterTemplate)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }

    //  Create a copy of the filter factory descriptor information and
    //  remove any pins and connections.  These will be added when
    //  the filter is initialized by BDAInitFilter.
    //
    pFilterDescriptor = ExAllocatePool( NonPagedPool,
                                        sizeof( KSFILTER_DESCRIPTOR)
                                        );
    if (!pFilterDescriptor)
    {
        status = STATUS_NO_MEMORY;
        goto errExit;
    }
    *pFilterDescriptor = *pInitialFilterDescriptor;
    pFilterDescriptor->PinDescriptorsCount = 0;
    pFilterDescriptor->PinDescriptors = NULL;
    pFilterDescriptor->NodeDescriptorsCount = 0;
    pFilterDescriptor->NodeDescriptors = NULL;
    pFilterDescriptor->ConnectionsCount = 0;
    pFilterDescriptor->Connections = NULL;
    
    status = KsMergeAutomationTables( 
                 &pNewAutomationTable,
                 (PKSAUTOMATION_TABLE) (pFilterDescriptor->AutomationTable),
                 (PKSAUTOMATION_TABLE) &BdaDefaultFilterAutomation,
                 NULL
                 );
    if (status != STATUS_SUCCESS)
    {
        goto errExit;
    }
    if (!pNewAutomationTable)
    {
        status = STATUS_NO_MEMORY;
        goto errExit;
    }
    pFilterDescriptor->AutomationTable = pNewAutomationTable;

    //$BUG - Check Filter Factory Dispatch for Filter Close.  If none
    //$BUG - we must add BdaDeleteFilterFactory to clean up.
    
    //  Create the KSFilterFactory
    //
    status = KsCreateFilterFactory(
                pKSDevice->FunctionalDeviceObject,
                pFilterDescriptor,
                NULL,   // RefString
                NULL,   // SecurityDescriptor
                0,      // CreateItemFlags
                NULL,   // SleepCallback
                NULL,   // WakeCallback
                &pKSFilterFactory
                );

    if ((status != STATUS_SUCCESS) || !pKSFilterFactory)
    {
        goto errExit;
    }


    //  Add our copy of the Filter Factory's new automation table to the
    //  KSFilterFactory's object bag.  This insures the memory will
    //  be freed when the filter factory is destroyed.
    //
    if (   pNewAutomationTable
        && (pNewAutomationTable != &BdaDefaultFilterAutomation)
       )
    {
        KsAddItemToObjectBag( pKSFilterFactory->Bag,
                              pNewAutomationTable,
                              NULL
                              );
    }
    pNewAutomationTable = NULL;


    //  Add our copy of the Filter Factory's descriptor to the
    //  KSFilterFactory's object bag.  This insures the memory will
    //  be freed when the filter factory is destroyed.
    //
    KsAddItemToObjectBag( pKSFilterFactory->Bag,
                          pFilterDescriptor,
                          NULL
                          );
    pFilterDescriptor = NULL;


    //  Merge our default filter automation table onto the filter
    //  factory descriptor
    //
    status = KsEdit( pKSFilterFactory, 
                     &(pKSFilterFactory->FilterDescriptor->AutomationTable),
                     'SadB'
                     );
    if (status != STATUS_SUCCESS)
    {
        goto errExit;
    }


    //  Create a filter factory context for BdaSup to use.
    //
    status = BdaCreateFilterFactoryContext( pKSFilterFactory,
                                            &pFilterFactoryCtx
                                            );
    if ((status != STATUS_SUCCESS) || !pFilterFactoryCtx)
    {
        KsDeleteFilterFactory( pKSFilterFactory);
        goto errExit;
    }

    //  Allow for the filter factory to use a default filter template
    //  topology when it creates a filter
    //
    //$REVIEW
    pFilterFactoryCtx->pInitialFilterDescriptor = pInitialFilterDescriptor;
    pFilterFactoryCtx->pBdaFilterTemplate = pBdaFilterTemplate;
    pFilterFactoryCtx->pKSFilterFactory = pKSFilterFactory;

    if (ppKSFilterFactory)
    {
        *ppKSFilterFactory = pKSFilterFactory;
    }

errExit:
    if (pFilterDescriptor)
    {
        ExFreePool( pFilterDescriptor);
        pFilterDescriptor = NULL;
    }

    if (   pNewAutomationTable
        && (pNewAutomationTable != &BdaDefaultFilterAutomation)
       )
    {
        ExFreePool( pNewAutomationTable);
        pNewAutomationTable = NULL;
    }

    return status;
}


/*
**  BdaCreateFilterFactory()
**
**  Initializes the common BDA filter context.
**
** Arguments:
**
**
** Returns:
**
**
**
** Side Effects:  none
*/

STDMETHODIMP_(NTSTATUS)
BdaCreateFilterFactory(
    PKSDEVICE                       pKSDevice,
    const KSFILTER_DESCRIPTOR *     pInitialFilterDescriptor,
    const BDA_FILTER_TEMPLATE *     pBdaFilterTemplate
    )
{
    NTSTATUS    Status = STATUS_SUCCESS;

    Status = BdaCreateFilterFactoryEx( pKSDevice,
                                       pInitialFilterDescriptor,
                                       pBdaFilterTemplate,
                                       NULL
                                       );
    return Status;
}


/*
**  BdaFilterFactoryUpdateCacheData()
**
**  Updates the pin data cache for the given filter factory.
**  The function will update the cached information for all pin factories
**  exposed by the given filter factory.  
**  
**  If the option filter descriptor is given, the function will update
**  the pin data cache for all pins listed in the given filter descriptor
**  instead of those in the filter factory.
**
**  Drivers will call this to update the pin data cache for all
**  pins that may be exposed by the filter factory.  The driver will
**  provide a filter descriptor listing pins that are not initially exposed
**  by the filter factory (this is usually the same as the template filter
**  descriptor).
**
** Arguments:
**
**
** Returns:
**
**
**
** Side Effects:  none
*/

STDMETHODIMP_(NTSTATUS)
BdaFilterFactoryUpdateCacheData(
    IN PKSFILTERFACTORY             pFilterFactory,
    IN const KSFILTER_DESCRIPTOR *  pFilterDescriptor OPTIONAL
    )
{
    NTSTATUS    Status = STATUS_SUCCESS;

    Status = KsFilterFactoryUpdateCacheData( pFilterFactory,
                                             pFilterDescriptor
                                             );

    return Status;
}


/*
**  BdaSyncTopology()
**
**  This routine updates the existing topology to complete all
**  Pending topology changes.
**
** Arguments:
**
**
** Returns:
**
**
**
** Side Effects:  none
*/

STDMETHODIMP_(NTSTATUS)
BdaSyncTopology(
    PKSFILTER       pKSFilter
    )
{
    NTSTATUS    Status = STATUS_SUCCESS;

    //$BUG  Implement topology sync.

    return STATUS_NOT_IMPLEMENTED;
}


// -------------------------------------------------------------------
// BDA Filter Global Property Set functions
// -------------------------------------------------------------------


/*
** BdaPropertyNodeTypes ()
**
**    Returns a list of ULONGS.
**
** Arguments:
**
**
** Returns:
**
** Side Effects:  none
*/
STDMETHODIMP_(NTSTATUS)
BdaPropertyNodeTypes(
    IN PIRP         Irp,
    IN PKSPROPERTY  Property,
    OUT ULONG *     pulProperty
    )
{
    NTSTATUS                    status = STATUS_SUCCESS;
    PKSFILTER                   pKSFilter;
    PBDA_FILTER_CONTEXT         pFilterCtx;
    const KSFILTER_DESCRIPTOR * pTemplateDesc;

    ASSERT( Irp);
    if (!Irp)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }
    ASSERT( Property);
    if (!Property)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }

    pKSFilter = KsGetFilterFromIrp( Irp);

    status = BdaGetFilterContext( pKSFilter, &pFilterCtx);

    if (status != STATUS_SUCCESS)
    {
        goto errExit;
    }

    ASSERT( pFilterCtx);
    ASSERT( pFilterCtx->pBdaFilterTemplate);
    pTemplateDesc = pFilterCtx->pBdaFilterTemplate->pFilterDescriptor;
    ASSERT( pTemplateDesc);
    ASSERT( pTemplateDesc->NodeDescriptorSize == sizeof( KSNODE_DESCRIPTOR));

    if (pulProperty)
    {
        ULONG                       uliNodeDesc;

        for ( uliNodeDesc = 0
            ; uliNodeDesc < pTemplateDesc->NodeDescriptorsCount
            ; uliNodeDesc++, pulProperty++
            )
        {
            //  For this implementation, the NodeType is just the
            //  index into the NodeDescriptor table.
            //
            *pulProperty = uliNodeDesc;
        }

        Irp->IoStatus.Information = uliNodeDesc * sizeof( ULONG);
    }
    else
    {
        status = STATUS_MORE_ENTRIES;

        //  If there is no place to put the property then just
        //  return the data size.
        //
        Irp->IoStatus.Information
            = pTemplateDesc->NodeDescriptorsCount * sizeof( ULONG);
    }


errExit:
    return status;
}


/*
** BdaPropertyNodeDescriptors ()
**
**    Returns a list of GUIDS.  The index of the GUID in the list
**    corresponds to the Node type.
**
** Arguments:
**
**
** Returns:
**
** Side Effects:  none
*/
STDMETHODIMP_(NTSTATUS)
BdaPropertyNodeDescriptors(
    IN PIRP                     Irp,
    IN PKSPROPERTY              Property,
    OUT BDANODE_DESCRIPTOR *    pNodeDescripterProperty
    )
{
    NTSTATUS                    status = STATUS_SUCCESS;
    PKSFILTER                   pKSFilter;
    PBDA_FILTER_CONTEXT         pFilterCtx;
    const KSFILTER_DESCRIPTOR * pTemplateDesc;
    ULONG                       ulcPropertyEntries;
    ULONG                       ulcNodes;

    ASSERT( Irp);
    if (!Irp)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }
    ASSERT( Property);
    if (!Property)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }

    //  Determine how many entries the input buffer can hold.
    //
    ulcPropertyEntries = OutputBufferLenFromIrp( Irp);
    ulcPropertyEntries = ulcPropertyEntries / sizeof( BDANODE_DESCRIPTOR);

    pKSFilter = KsGetFilterFromIrp( Irp);

    status = BdaGetFilterContext( pKSFilter, &pFilterCtx);

    if (status != STATUS_SUCCESS)
    {
        goto errExit;
    }

    ASSERT( pFilterCtx);

    if (   !pFilterCtx
        || !pFilterCtx->pBdaFilterTemplate
       )
    {
        status = STATUS_INVALID_DEVICE_STATE;
        goto errExit;
    }

    pTemplateDesc = pFilterCtx->pBdaFilterTemplate->pFilterDescriptor;
    ASSERT( pTemplateDesc);
    if (!pTemplateDesc)
    {
        status = STATUS_INVALID_DEVICE_STATE;
        goto errExit;
    }

    ASSERT( pTemplateDesc->NodeDescriptorSize == sizeof( KSNODE_DESCRIPTOR));

    //  Handle the case of a NULL NodeDesriptor array as 0 nodes.
    //
    if (!pTemplateDesc->NodeDescriptors)
    {
        ulcNodes = 0;
    }
    else
    {
        ulcNodes = pTemplateDesc->NodeDescriptorsCount;
    }

    if (!pNodeDescripterProperty || (ulcPropertyEntries < ulcNodes))
    {
        status = STATUS_MORE_ENTRIES;

        //  If there is no place to put the property then just
        //  return the data size.
        //
    }
    else
    {
        const KSNODE_DESCRIPTOR *   pNodeDesc;
        ULONG                       uliNodeDesc;
    
        pNodeDesc = pTemplateDesc->NodeDescriptors;

        if (pNodeDesc)
        {
            for ( uliNodeDesc = 0
                ; uliNodeDesc < ulcNodes
                ; uliNodeDesc++, pNodeDescripterProperty++
                )
            {
                //  For this implementation, the NodeType is just the
                //  index into the NodeDescriptor table.
                //
                pNodeDescripterProperty->ulBdaNodeType = uliNodeDesc;

                //  Fill in the function GUID for the node type.
                //  
                if (pNodeDesc->Type)
                {
                    pNodeDescripterProperty->guidFunction = *pNodeDesc->Type;
                }
                else 
                {
                    pNodeDescripterProperty->guidFunction = GUID_NULL;
                }

                //  Fill in the GUID that represents a displayable name
                //  for the node type.
                if (pNodeDesc->Name)
                {
                    pNodeDescripterProperty->guidName = *pNodeDesc->Name;
                }
                else
                {
                    pNodeDescripterProperty->guidName = GUID_NULL;
                }

                //  Point at the next node descriptor
                //
                pNodeDesc = (const KSNODE_DESCRIPTOR *)
                            ((BYTE *) pNodeDesc + pTemplateDesc->NodeDescriptorSize);
            }
        }
    }

    Irp->IoStatus.Information = ulcNodes * sizeof( BDANODE_DESCRIPTOR);


errExit:
    return status;
}


/*
** BdaPropertyNodeProperties ()
**
**    Returns a list of GUIDS.  The guid for each property set
**    supported by the specified node is included in the list.
**
** Arguments:
**
**
** Returns:
**
** Side Effects:  none
*/
STDMETHODIMP_(NTSTATUS)
BdaPropertyNodeProperties(
    IN PIRP         Irp,
    IN PKSP_NODE    Property,
    OUT GUID *      pguidProperty
    )
{
    NTSTATUS                    status = STATUS_SUCCESS;
    PKSFILTER                   pKSFilter;
    PBDA_FILTER_CONTEXT         pFilterCtx;
    const KSFILTER_DESCRIPTOR * pTemplateDesc;
    const KSNODE_DESCRIPTOR *   pNodeDesc;
    const KSAUTOMATION_TABLE*   pAutomationTable;
    ULONG                       uliNodeDesc;
    ULONG                       ulcInterfaces;
    ULONG                       ulcPropertyEntries;

    ASSERT( Irp);
    if (!Irp)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }
    ASSERT( Property);
    if (!Property)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }

    //  Determine how many entries the input buffer can hold.
    //
    ulcPropertyEntries = OutputBufferLenFromIrp( Irp);
    ulcPropertyEntries = ulcPropertyEntries / sizeof( GUID);

    pKSFilter = KsGetFilterFromIrp( Irp);
    if (!pKSFilter)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }

    status = BdaGetFilterContext( pKSFilter, &pFilterCtx);
    if (status != STATUS_SUCCESS)
    {
        goto errExit;
    }
    ASSERT( pFilterCtx);
    ASSERT( pFilterCtx->pBdaFilterTemplate);
    if (!pFilterCtx->pBdaFilterTemplate)
    {
        status = STATUS_INVALID_DEVICE_STATE;
        goto errExit;
    }
    pTemplateDesc = pFilterCtx->pBdaFilterTemplate->pFilterDescriptor;
    ASSERT( pTemplateDesc);
    if (!pTemplateDesc)
    {
        status = STATUS_INVALID_DEVICE_STATE;
        goto errExit;
    }
    ASSERT( pTemplateDesc->NodeDescriptorSize == sizeof( KSNODE_DESCRIPTOR));

    uliNodeDesc = Property->NodeId;

    if (uliNodeDesc >= pTemplateDesc->NodeDescriptorsCount)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }

    pNodeDesc = pTemplateDesc->NodeDescriptors;
    ASSERT( pNodeDesc);
    if (!pNodeDesc)
    {
        status = STATUS_INVALID_DEVICE_STATE;
        goto errExit;
    }

    ASSERT( pTemplateDesc->NodeDescriptorSize);
    pNodeDesc = (const KSNODE_DESCRIPTOR *)
                ((BYTE *) pNodeDesc + uliNodeDesc * pTemplateDesc->NodeDescriptorSize);

    if (   !pNodeDesc->AutomationTable
        || !pNodeDesc->AutomationTable->PropertySets
       )
    {
        ulcInterfaces =  0;
    }
    else
    {
        ulcInterfaces =  pNodeDesc->AutomationTable->PropertySetsCount;
    }

    if (!pguidProperty || (ulcPropertyEntries < ulcInterfaces))
    {
        status = STATUS_MORE_ENTRIES;

        //  If there is no place to put the property then just
        //  return the data size.
        //
    }
    else
    {
        ULONG                   uliSet;
        const KSPROPERTY_SET *  pPropertySet;
        GUID *                  pguidOut;

        pguidOut = pguidProperty;
        
        pPropertySet = pNodeDesc->AutomationTable->PropertySets;
        if (pPropertySet)
        {
            for ( uliSet = 0
                ; uliSet < ulcInterfaces
                ; uliSet++
                )
            {
                RtlMoveMemory( pguidOut,
                               pPropertySet->Set,
                               sizeof( GUID)
                               );
                pguidOut += 1;
                pPropertySet += 1;
            }
        }
    }

    Irp->IoStatus.Information = ulcInterfaces * sizeof( GUID);

errExit:
    return status;
}


/*
** BdaPropertyNodeMethods ()
**
**    Returns a list of GUIDS.  The guid for each property set
**    supported by the specified node is included in the list.
**
** Arguments:
**
**
** Returns:
**
** Side Effects:  none
*/
STDMETHODIMP_(NTSTATUS)
BdaPropertyNodeMethods(
    IN PIRP         Irp,
    IN PKSP_NODE    Property,
    OUT GUID *      pguidProperty
    )
{
    NTSTATUS                    status = STATUS_SUCCESS;
    PKSFILTER                   pKSFilter;
    PBDA_FILTER_CONTEXT         pFilterCtx;
    const KSFILTER_DESCRIPTOR * pTemplateDesc;
    const KSNODE_DESCRIPTOR *   pNodeDesc;
    const KSAUTOMATION_TABLE*   pAutomationTable;
    ULONG                       uliNodeDesc;
    ULONG                       ulcInterfaces;

    ASSERT( Irp);
    if (!Irp)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }
    ASSERT( Property);
    if (!Property)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }

    pKSFilter = KsGetFilterFromIrp( Irp);

    status = BdaGetFilterContext( pKSFilter, &pFilterCtx);

    if (status != STATUS_SUCCESS)
    {
        goto errExit;
    }

    ASSERT( pFilterCtx);
    ASSERT( pFilterCtx->pBdaFilterTemplate);
    pTemplateDesc = pFilterCtx->pBdaFilterTemplate->pFilterDescriptor;
    ASSERT( pTemplateDesc);
    ASSERT( pTemplateDesc->NodeDescriptorSize == sizeof( KSNODE_DESCRIPTOR));

    uliNodeDesc = Property->NodeId;

    if (uliNodeDesc >= pTemplateDesc->NodeDescriptorsCount)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }

    pNodeDesc = pTemplateDesc->NodeDescriptors;
    ASSERT( pTemplateDesc->NodeDescriptorSize);
    pNodeDesc = (const KSNODE_DESCRIPTOR *)
                ((BYTE *) pNodeDesc + uliNodeDesc * pTemplateDesc->NodeDescriptorSize);

    ulcInterfaces =  pNodeDesc->AutomationTable->PropertySetsCount;

    if (pguidProperty)
    {
        ULONG                   uliSet;
        const KSMETHOD_SET *    pMethodSet;
        GUID *                  pguidOut;

        pguidOut = pguidProperty;
        ulcInterfaces = 0;
        
        pMethodSet = pNodeDesc->AutomationTable->MethodSets;
        if (pMethodSet)
        {
            for ( uliSet = 0
                ; uliSet < pNodeDesc->AutomationTable->MethodSetsCount
                ; uliSet++
                )
            {
                RtlMoveMemory( pguidOut,
                               pMethodSet->Set,
                               sizeof( GUID)
                               );
                pguidOut += 1;
                pMethodSet += 1;
                ulcInterfaces += 1;
            }
        }

        Irp->IoStatus.Information = ulcInterfaces * sizeof( GUID);
    }
    else
    {
        status = STATUS_MORE_ENTRIES;

        //  If there is no place to put the property then just
        //  return the data size.
        //
        Irp->IoStatus.Information = ulcInterfaces * sizeof( GUID);
    }


errExit:
    return status;
}


/*
** BdaPropertyNodeEvents ()
**
**    Returns a list of GUIDS.  The guid for each event set
**    supported by the specified node is included in the list.
**
** Arguments:
**
**
** Returns:
**
** Side Effects:  none
*/
STDMETHODIMP_(NTSTATUS)
BdaPropertyNodeEvents(
    IN PIRP         Irp,
    IN PKSP_NODE    Property,
    OUT GUID *      pguidProperty
    )
{
    NTSTATUS                    status = STATUS_SUCCESS;
    PKSFILTER                   pKSFilter;
    PBDA_FILTER_CONTEXT         pFilterCtx;
    const KSFILTER_DESCRIPTOR * pTemplateDesc;
    const KSNODE_DESCRIPTOR *   pNodeDesc;
    const KSAUTOMATION_TABLE*   pAutomationTable;
    ULONG                       uliNodeDesc;
    ULONG                       ulcInterfaces;

    ASSERT( Irp);
    if (!Irp)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }
    ASSERT( Property);
    if (!Property)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }

    pKSFilter = KsGetFilterFromIrp( Irp);

    status = BdaGetFilterContext( pKSFilter, &pFilterCtx);

    if (status != STATUS_SUCCESS)
    {
        goto errExit;
    }

    ASSERT( pFilterCtx);
    ASSERT( pFilterCtx->pBdaFilterTemplate);
    pTemplateDesc = pFilterCtx->pBdaFilterTemplate->pFilterDescriptor;
    ASSERT( pTemplateDesc);
    ASSERT( pTemplateDesc->NodeDescriptorSize == sizeof( KSNODE_DESCRIPTOR));

    uliNodeDesc = Property->NodeId;

    if (uliNodeDesc >= pTemplateDesc->NodeDescriptorsCount)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }

    pNodeDesc = pTemplateDesc->NodeDescriptors;
    ASSERT( pTemplateDesc->NodeDescriptorSize);
    pNodeDesc = (const KSNODE_DESCRIPTOR *)
                ((BYTE *) pNodeDesc + uliNodeDesc * pTemplateDesc->NodeDescriptorSize);

    ulcInterfaces =  pNodeDesc->AutomationTable->PropertySetsCount;

    if (pguidProperty)
    {
        ULONG                   uliSet;
        const KSEVENT_SET *     pEventSet;
        GUID *                  pguidOut;

        pguidOut = pguidProperty;
        ulcInterfaces = 0;
        
        pEventSet = pNodeDesc->AutomationTable->EventSets;
        if (pEventSet)
        {
            for ( uliSet = 0
                ; uliSet < pNodeDesc->AutomationTable->EventSetsCount
                ; uliSet++
                )
            {
                RtlMoveMemory( pguidOut,
                               pEventSet->Set,
                               sizeof( GUID)
                               );
                pguidOut += 1;
                pEventSet += 1;
                ulcInterfaces += 1;
            }
        }

        Irp->IoStatus.Information = ulcInterfaces * sizeof( GUID);
    }
    else
    {
        status = STATUS_MORE_ENTRIES;

        //  If there is no place to put the property then just
        //  return the data size.
        //
        Irp->IoStatus.Information = ulcInterfaces * sizeof( GUID);
    }


errExit:
    return status;
}


/*
** BdaPropertyPinTypes ()
**
**    Returns a list of GUIDS.  The index of the GUID in the list
**    corresponds to the Node type.
**
** Arguments:
**
**
** Returns:
**
** Side Effects:  none
*/
STDMETHODIMP_(NTSTATUS)
BdaPropertyPinTypes(
    IN PIRP         Irp,
    IN PKSPROPERTY  Property,
    OUT ULONG *     pulProperty
    )
{
    NTSTATUS                    status = STATUS_SUCCESS;
    PKSFILTER                   pKSFilter;
    PBDA_FILTER_CONTEXT         pFilterCtx;
    const KSFILTER_DESCRIPTOR * pTemplateDesc;
    const KSPIN_DESCRIPTOR_EX * pPinDesc;
    ULONG                       uliPinDesc;

    ASSERT( Irp);
    if (!Irp)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }
    ASSERT( Property);
    if (!Property)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }

    pKSFilter = KsGetFilterFromIrp( Irp);

    status = BdaGetFilterContext( pKSFilter, &pFilterCtx);

    if (status != STATUS_SUCCESS)
    {
        goto errExit;
    }

    ASSERT( pFilterCtx);
    ASSERT( pFilterCtx->pBdaFilterTemplate);
    pTemplateDesc = pFilterCtx->pBdaFilterTemplate->pFilterDescriptor;
    ASSERT( pTemplateDesc);
    ASSERT( pTemplateDesc->PinDescriptorSize == sizeof( KSPIN_DESCRIPTOR_EX));

    if (pulProperty)
    {
        for ( uliPinDesc = 0
            ; uliPinDesc < pTemplateDesc->PinDescriptorsCount
            ; uliPinDesc++, pulProperty++
            )
        {
            //  For this implementation, the PinType is just the
            //  index into the PinDescriptor table.
            //
            *pulProperty = uliPinDesc;
        }

        Irp->IoStatus.Information = uliPinDesc * sizeof( ULONG);
    }
    else
    {
        status = STATUS_BUFFER_OVERFLOW;

        //  If there is no place to put the property then just
        //  return the data size.
        //
        Irp->IoStatus.Information
            = pTemplateDesc->PinDescriptorsCount * sizeof( ULONG);
    }

errExit:
    return status;
}


/*
** BdaPropertyTemplateConnections ()
**
**    Returns a list of KSTOPOLOGY_CONNECTIONS.  The list of connections
**    describs how pin types and node types are connected in the template
**    topology
**
** Arguments:
**
**
** Returns:
**
** Side Effects:  none
*/
STDMETHODIMP_(NTSTATUS)
BdaPropertyTemplateConnections(
    IN PIRP                     Irp,
    IN PKSPROPERTY              Property,
    OUT PKSTOPOLOGY_CONNECTION  pConnectionProperty
    )
{
    NTSTATUS                        status = STATUS_SUCCESS;
    PKSFILTER                       pKSFilter;
    PBDA_FILTER_CONTEXT             pFilterCtx;
    const KSFILTER_DESCRIPTOR *     pTemplateDesc;
    const KSTOPOLOGY_CONNECTION *   pConnection;
    ULONG                           uliConnection;

    ASSERT( Irp);
    if (!Irp)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }
    ASSERT( Property);
    if (!Property)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }

    pKSFilter = KsGetFilterFromIrp( Irp);

    status = BdaGetFilterContext( pKSFilter, &pFilterCtx);

    if (status != STATUS_SUCCESS)
    {
        goto errExit;
    }

    ASSERT( pFilterCtx);
    ASSERT( pFilterCtx->pBdaFilterTemplate);
    pTemplateDesc = pFilterCtx->pBdaFilterTemplate->pFilterDescriptor;
    ASSERT( pTemplateDesc);


    if (pConnectionProperty)
    {
        for ( uliConnection = 0, pConnection = pTemplateDesc->Connections
            ; uliConnection < pTemplateDesc->ConnectionsCount
            ; uliConnection++, pConnection++, pConnectionProperty++
            )
        {
            *pConnectionProperty = *pConnection;
        }

        Irp->IoStatus.Information
            = uliConnection * sizeof( KSTOPOLOGY_CONNECTION);
    }
    else
    {
        status = STATUS_BUFFER_OVERFLOW;

        //  If there is no place to put the property then just
        //  return the data size.
        //
        Irp->IoStatus.Information
            = pTemplateDesc->ConnectionsCount * sizeof( KSTOPOLOGY_CONNECTION);
    }

errExit:
    return status;
}


/*
** BdaPinTypeFromPinId()
**
**    Gets the ID of the pin on which to submit node properties, methods
**    and events.
**
** Arguments:
**
**
** Returns:
**
** Side Effects:  none
*/
STDMETHODIMP_(NTSTATUS)
BdaPinTypeFromPinId(
    PBDA_FILTER_CONTEXT         pFilterCtx,
    ULONG                       ulPinId,
    PULONG                      pulPinType
    )
{
    NTSTATUS                        status = STATUS_SUCCESS;

    if (   !pFilterCtx
        || !pFilterCtx->argPinFactoryCtx
        || (ulPinId >= pFilterCtx->ulcPinFactories)
       )
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }

    if (pFilterCtx->argPinFactoryCtx[ulPinId].ulPinFactoryId != ulPinId)
    {
        status = STATUS_NOT_FOUND;
        goto errExit;
    }

    *pulPinType = pFilterCtx->argPinFactoryCtx[ulPinId].ulPinType;

errExit:
    return status;
}

/*
** BdaGetControllingPinId ()
**
**    Gets the ID of the pin on which to submit node properties, methods
**    and events.
**
** Arguments:
**
**
** Returns:
**
** Side Effects:  none
*/
STDMETHODIMP_(NTSTATUS)
BdaGetControllingPinId(
    PBDA_FILTER_CONTEXT         pFilterCtx,
    ULONG                       ulInputPinId,
    ULONG                       ulOutputPinId,
    ULONG                       ulNodeType,
    PULONG                      pulControllingPinId
    )
{
    NTSTATUS                        status = STATUS_SUCCESS;
    ULONG                           ulInputPinType = 0;
    ULONG                           ulOutputPinType = -1;
    ULONG                           ulControllingPinType = -1;

    //  Get the input pin type.
    //
    status = BdaPinTypeFromPinId( pFilterCtx, 
                                  ulInputPinId,
                                  &ulInputPinType
                                  );
    if (status != STATUS_SUCCESS)
    {
        goto errExit;
    }

    //  Get the output pin type.
    //
    status = BdaPinTypeFromPinId( pFilterCtx, 
                                  ulOutputPinId,
                                  &ulOutputPinType
                                  );
    if (status != STATUS_SUCCESS)
    {
        goto errExit;
    }

    //  Determine the cotnrolling pin type.
    //
    status = BdaGetControllingPinType( ulNodeType,
                                       ulInputPinType,
                                       ulOutputPinType,
                                       pFilterCtx,
                                       &ulControllingPinType
                                       );
    if (status != STATUS_SUCCESS)
    {
        goto errExit;
    }

    //  Map the controlling pin type to the controlling pin ID.
    //
    if (ulControllingPinType == ulInputPinType)
    {
        *pulControllingPinId = ulInputPinId;
    }
    else if (ulControllingPinType == ulOutputPinType)
    {
        *pulControllingPinId = ulOutputPinId;
    }
    else
    {
        status = STATUS_NOT_FOUND;
    }


errExit:

    return status;
}

/*
** BdaPropertyGetControllingPinId ()
**
**    Gets the ID of the pin on which to submit node properties, methods
**    and events.
**
** Arguments:
**
**
** Returns:
**
** Side Effects:  none
*/
STDMETHODIMP_(NTSTATUS)
BdaPropertyGetControllingPinId(
    IN PIRP                     Irp,
    IN PKSP_BDA_NODE_PIN        Property,
    OUT PULONG                  pulControllingPinId
    )
{
    NTSTATUS                        status = STATUS_SUCCESS;
    PKSFILTER                       pKSFilter;
    PBDA_FILTER_CONTEXT             pFilterCtx;

    ASSERT( Irp);
    if (!Irp)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }
    ASSERT( Property);
    if (!Property)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }

    pKSFilter = KsGetFilterFromIrp( Irp);

    status = BdaGetFilterContext( pKSFilter, &pFilterCtx);

    if (status != STATUS_SUCCESS)
    {
        goto errExit;
    }

    ASSERT( pFilterCtx);
    if (!pFilterCtx)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }

    if (pulControllingPinId)
    {
        status = BdaGetControllingPinId(
                                        pFilterCtx,
                                        Property->ulInputPinId,
                                        Property->ulOutputPinId,
                                        Property->ulNodeType,
                                        pulControllingPinId
                                        );

        if (status == STATUS_NOT_FOUND)
        {
            //  See if the pins were part of a static filter configuration.
            //
            //$BUG - Pins that are configured without template type information
            //       should always be controlled by the output pin.
            //
            if (Property->ulNodeType == 0)
            {
                *pulControllingPinId = Property->ulInputPinId;
            }
            else
            {
                *pulControllingPinId = Property->ulOutputPinId;
            }

            status = STATUS_SUCCESS;
        }

        Irp->IoStatus.Information = sizeof( ULONG);
    }
    else
    {
        status = STATUS_BUFFER_OVERFLOW;

        //  If there is no place to put the property then just
        //  return the data size.
        //
        Irp->IoStatus.Information = sizeof( ULONG);
    }

errExit:
    return status;
}


/*
** BdaStartChanges ()
**
**    Puts the filter into change state.  All changes to BDA topology
**    and properties changed after this will be in effect only after
**    CommitChanges.
**
** Arguments:
**
**
** Returns:
**
** Side Effects:  none
*/
STDMETHODIMP_(NTSTATUS)
BdaStartChanges(
    IN PIRP         pIrp
    )
{
    ASSERT( pIrp);
    if (!pIrp)
    {
        return STATUS_INVALID_PARAMETER;
    }
    return STATUS_SUCCESS;
}


/*
** BdaCheckChanges ()
**
**    Checks the changes to BDA interfaces that have occured since the
**    last StartChanges.  Returns the result that would have occurred if
**    CommitChanges had been called.
**
** Arguments:
**
**
** Returns:
**
** Side Effects:  none
*/
STDMETHODIMP_(NTSTATUS)
BdaCheckChanges(
    IN PIRP         pIrp
    )
{
    ASSERT( pIrp);
    if (!pIrp)
    {
        return STATUS_INVALID_PARAMETER;
    }
    return STATUS_SUCCESS;
}


/*
** BdaCommitChanges ()
**
**    Checks the changes to BDA interfaces that have occured since the
**    last StartChanges.
**
** Arguments:
**
**
** Returns:
**
** Side Effects:  none
*/
STDMETHODIMP_(NTSTATUS)
BdaCommitChanges(
    IN PIRP         pIrp
    )
{
    ASSERT( pIrp);
    if (!pIrp)
    {
        return STATUS_INVALID_PARAMETER;
    }
    return STATUS_SUCCESS;
}


/*
** BdaGetChangeState ()
**
**    Checks the changes to BDA interfaces that have occured since the
**    last StartChanges.
**
** Arguments:
**
**
** Returns:
**
** Side Effects:  none
*/
STDMETHODIMP_(NTSTATUS)
BdaGetChangeState(
    IN PIRP             pIrp,
    PBDA_CHANGE_STATE   pChangeState
    )
{
    ASSERT( pIrp);
    if (!pIrp)
    {
        return STATUS_INVALID_PARAMETER;
    }
    ASSERT( pChangeState);
    if (!pChangeState)
    {
        return STATUS_INVALID_PARAMETER;
    }
    *pChangeState = BDA_CHANGES_COMPLETE;

    return STATUS_SUCCESS;
}


/*
** BdaMethodCreatePin ()
**
**    Creates a new pin factory for the given pin type.
**
** Arguments:
**
**
** Returns:
**
** Side Effects:  none
*/
STDMETHODIMP_(NTSTATUS)
BdaMethodCreatePin(
    IN PIRP         pIrp,
    IN PKSMETHOD    pKSMethod,
    OUT PULONG      pulPinFactoryID
    )
{
    NTSTATUS        status = STATUS_SUCCESS;

    ASSERT(pIrp);
    if (pIrp)
    {
        PKSFILTER       pKSFilter;
        PKSM_BDA_PIN    pKSPinMethod;
        ULONG           ulPinId;

        pKSPinMethod = (PKSM_BDA_PIN) pKSMethod;

        pKSFilter = KsGetFilterFromIrp( pIrp);
    
        if (pKSFilter && pKSPinMethod)
        {

            status = BdaCreatePin( pKSFilter,
                                   pKSPinMethod->PinType,
                                   pulPinFactoryID
                                   );
        }
    }
    else
    {
        status = STATUS_INVALID_PARAMETER;
    }

    return status;
}


/*
** BdaMethodDeletePin ()
**
**    Deletes the given pin factory
**
** Arguments:
**
**
** Returns:
**
** Side Effects:  none
*/
STDMETHODIMP_(NTSTATUS)
BdaMethodDeletePin(
    IN PIRP         Irp,
    IN PKSMETHOD    Method,
    OPTIONAL PVOID  pvIgnored
    )
{
    ASSERT( Irp);
    if (!Irp)
    {
        return STATUS_INVALID_PARAMETER;
    }
    return STATUS_SUCCESS;
}


/*
** BdaPropertyGetPinControl ()
**
**    Returns a the BDA ID or BDA Template Type of the Pin.
**
** Arguments:
**
**
** Returns:
**
** Side Effects:  none
*/
STDMETHODIMP_(NTSTATUS)
BdaPropertyGetPinControl(
    IN PIRP         Irp,
    IN PKSPROPERTY  Property,
    OUT ULONG *     pulProperty
    )
{
    NTSTATUS                    status = STATUS_SUCCESS;
    PKSPIN                      pKSPin;
    BDA_PIN_FACTORY_CONTEXT     pinCtx;

    ASSERT( Irp);
    if (!Irp)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }
    ASSERT( Property);
    if (!Property)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }

    pKSPin = KsGetPinFromIrp( Irp);

    status = BdaGetPinFactoryContext( pKSPin, &pinCtx);

    if (status != STATUS_SUCCESS)
    {
        goto errExit;
    }

    if (pulProperty)
    {
        Irp->IoStatus.Information = sizeof( ULONG);

        switch (Property->Id)
        {
        case KSPROPERTY_BDA_PIN_ID:
            //  Return the BDA ID of this pin
            //
            *pulProperty = pinCtx.ulPinFactoryId;
            break;

        case KSPROPERTY_BDA_PIN_TYPE:
            //  Return the BDA Type of this pin
            //
            *pulProperty = pinCtx.ulPinType;
            break;

        default:
            Irp->IoStatus.Information = 0;
            status = STATUS_INVALID_PARAMETER;
            ASSERT( FALSE);
        }
    }
    else
    {
        status = STATUS_MORE_ENTRIES;

        //  If there is no place to put the property then just
        //  return the data size.
        //
        Irp->IoStatus.Information = sizeof( ULONG);
    }


errExit:
    return status;
}


/*
** BdaValidateNodeProperty ()
**
**    Validates that the IRP is for a Pin and that
**    the property belongs to a node associated with that
**    Pin.
**
** Arguments:
**
**
** Returns:
**
** Side Effects:  none
*/
STDMETHODIMP_(NTSTATUS)
BdaValidateNodeProperty(
    IN PIRP         pIrp,
    IN PKSMETHOD    pKSProperty
    )
{
    ASSERT( pIrp);
    if (!pIrp)
    {
        return STATUS_INVALID_PARAMETER;
    }
    ASSERT( pKSProperty);
    if (!pKSProperty)
    {
        return STATUS_INVALID_PARAMETER;
    }
    return STATUS_SUCCESS;
}


/*
** BdaMethodCreateTopology ()
**
**    Creates the topology between the two given pin factories.
**
** Arguments:
**
**
** Returns:
**
** Side Effects:  none
*/
STDMETHODIMP_(NTSTATUS)
BdaMethodCreateTopology(
    IN PIRP         pIrp,
    IN PKSMETHOD    pKSMethod,
    OPTIONAL PVOID  pvIgnored
    )
{
    NTSTATUS            status = STATUS_SUCCESS;
    PKSFILTER           pKSFilter;
    PKSM_BDA_PIN_PAIR   pKSPinPairMethod;
    ULONG               ulPinId;

    if (pIrp)
    {
        pKSPinPairMethod = (PKSM_BDA_PIN_PAIR) pKSMethod;

        pKSFilter = KsGetFilterFromIrp( pIrp);
    
        if (pKSFilter && pKSPinPairMethod)
        {
            //  Obtain the KS Filter Mutex
            //
            //$BUG - Obtain the KS Filter Mutex

            status = BdaCreateTopology( pKSFilter,
                                        pKSPinPairMethod->InputPinId,
                                        pKSPinPairMethod->OutputPinId
                                        );
        
            //  Release the KS Filter Mutex
            //
            //$BUG - Obtain the KS Filter Mutex
        }
        else
        {
            status = STATUS_INVALID_PARAMETER;
        }
    }
    else
    {
        status = STATUS_INVALID_PARAMETER;
    }

    return status;
}


/*
**  BdaFindPinPair()
**
**      Returns a pointer to the BDA_PIN_PAIRING that corresponds
**      to the given input and output pins.
**
**  Arguments:
**
**      pTopology   Pointer to the BDA topology that contains the
**                  pin pairing.
**
**      InputPinId  Id of the input Pin to match
**
**      OutputPinId Id of the output Pin to match
**
**  Returns:
**
**      pPinPairing     Pointer to a valid BDA Pin Pairing structure
**
**      NULL            If no valid pin pairing exists with the
**                      given input and output pins.
**
** Side Effects:  none
*/

PBDA_PIN_PAIRING
BdaFindPinPair(
    PBDA_FILTER_TEMPLATE    pFilterTemplate,
    ULONG                   InputPinId,
    ULONG                   OutputPinId
    )
{
    return NULL;
}


/*
**  BdaGetPinFactoryContext()
**
**  Finds a BDA PinFactory Context that corresponds
**  to the given KS Pin Instance.
**
** Arguments:
**
**
** Returns:
**
**
**
** Side Effects:  none
*/

STDMETHODIMP_(NTSTATUS)
BdaGetPinFactoryContext(
    PKSPIN                          pKSPin,
    PBDA_PIN_FACTORY_CONTEXT        pPinFactoryCtx
    )
{
    NTSTATUS                    status = STATUS_SUCCESS;
    PKSFILTER                   pKSFilter;
    PBDA_FILTER_CONTEXT         pFilterCtx = NULL;

    if (!pKSPin || !pPinFactoryCtx)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }

    pKSFilter = KsPinGetParentFilter( pKSPin);
    if (!pKSFilter)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }


    //  Find our Filter Context so that we can look up the pin type
    //  in the Template Topology.
    //
    status = BdaGetFilterContext( pKSFilter, &pFilterCtx);
    if (status != STATUS_SUCCESS)
    {
        goto errExit;
    }
    ASSERT( pFilterCtx);

    if (pKSPin->Id >= pFilterCtx->ulcPinFactories)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }

    *pPinFactoryCtx = pFilterCtx->argPinFactoryCtx[pKSPin->Id];

errExit:
    return status;
}


/*
**  BdaCreatePin()
**
**      Utility function creates a new pin in the given filter instance.
**
**
**  Arguments:
**
**
**
**      PinType         Pin type to create.
**
**      pPinId          Id of the Pin that was created.
**
**  Returns:
**
**
** Side Effects:  none
*/

STDMETHODIMP_(NTSTATUS)
BdaCreatePin(
    PKSFILTER                   pKSFilter,
    ULONG                       ulPinType,
    PULONG                      pulPinId
    )
{
    NTSTATUS                        status = STATUS_SUCCESS;
    PBDA_FILTER_CONTEXT             pFilterCtx;
    PBDA_PIN_FACTORY_CONTEXT        pPinFactoryCtx;
    KSPIN_DESCRIPTOR_EX             myKSPinDescriptorEx;
    KSAUTOMATION_TABLE *            pNewAutomationTable = NULL;
    const KSPIN_DESCRIPTOR_EX *     pKSPinDescriptorEx;
    const BDA_FILTER_TEMPLATE *     pBdaFilterTemplate;
    const KSFILTER_DESCRIPTOR *     pFilterDescriptor;


    ASSERT( pulPinId);
    if (!pulPinId)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }

    //  Find our Filter Context so that we can look up the pin type
    //  in the Template Topology.
    //
    status = BdaGetFilterContext( pKSFilter, &pFilterCtx);
    if (status != STATUS_SUCCESS)
    {
        goto errExit;
    }
    ASSERT( pFilterCtx);

    //  Locate this filter's Template Topology
    //
    if (   !pFilterCtx
        || !pFilterCtx->pBdaFilterTemplate
        || !pFilterCtx->pBdaFilterTemplate->pFilterDescriptor
       )
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }
    pFilterDescriptor = pFilterCtx->pBdaFilterTemplate->pFilterDescriptor;

    //  Locate the Pin Type in this filter's Template Topology
    //
    if (pFilterDescriptor->PinDescriptorsCount <= ulPinType)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }

    //  Get the KSPIN_DESCRIPTOR_EX for this pin type
    //
    pKSPinDescriptorEx = pFilterDescriptor->PinDescriptors;
    ASSERT( pKSPinDescriptorEx);
    ASSERT( pFilterDescriptor->PinDescriptorSize);
    pKSPinDescriptorEx = (const KSPIN_DESCRIPTOR_EX *)
                         (  (BYTE *) pKSPinDescriptorEx
                          + ulPinType * pFilterDescriptor->PinDescriptorSize
                         );

    //  Create a new copy of the pin descriptor so that it is easier to
    //  modify
    //
    myKSPinDescriptorEx = *pKSPinDescriptorEx;
    myKSPinDescriptorEx.AutomationTable = NULL;

    status = BdaAddNodeAutomationToPin( pFilterCtx, 
                                        ulPinType,
                                        pKSFilter->Bag,
                                        pKSPinDescriptorEx->AutomationTable,
                                        &pNewAutomationTable
                                      );
    if (status != STATUS_SUCCESS)
    {
        goto errExit;
    }

    //  Merge required properties for which BdaSup.sys provides a default
    //  implementation.
    //
    status = KsMergeAutomationTables( 
                 &((PKSAUTOMATION_TABLE)(myKSPinDescriptorEx.AutomationTable)),
                 pNewAutomationTable,
                 (PKSAUTOMATION_TABLE) &BdaDefaultPinAutomation,
                 pKSFilter->Bag
                 );
    if (status != STATUS_SUCCESS)
    {
        goto errExit;
    }


    status = KsFilterCreatePinFactory ( pKSFilter,
                                        &myKSPinDescriptorEx,
                                        pulPinId
                                      );
    if (status != STATUS_SUCCESS)
    {
        goto errExit;
    }

    status = BdaCreatePinFactoryContext( pKSFilter,
                                         pFilterCtx,
                                         *pulPinId,
                                         ulPinType
                                         );

errExit:

    return status;
}


/*
**  BdaAddNodeAutomationToPin()
**
**      Merges the automation tables for each node type that is controlled
**      by the pin type being created into the automation table for the
**      the pin factory.  This is how the automation tables for BDA
**      control nodes get linked to the controlling pin.  Otherwise the
**      nodes would not be accesable.
**
**
**  Arguments:
**
**
**      pFilterCtx      The BDA filter context to which the pin factory
**                      belongs.  Must have this to get at the template
**                      topology.
**
**      ulPinType       BDA Pin Type of the pin being created.  Need this
**                      to figure out which nodes are controlled by the
**                      pin.
**
**  Returns:
**      Always returns a resulting automation table, even on error.
**
**
** Side Effects:  none
*/

STDMETHODIMP_(NTSTATUS)
BdaAddNodeAutomationToPin( 
    PBDA_FILTER_CONTEXT         pFilterCtx, 
    ULONG                       ulControllingPinType,
    KSOBJECT_BAG                ObjectBag,
    const KSAUTOMATION_TABLE *  pOriginalAutomationTable,
    PKSAUTOMATION_TABLE *       ppNewAutomationTable
    )
{
    NTSTATUS                    status = STATUS_SUCCESS;
    const KSFILTER_DESCRIPTOR * pFilterDescriptor;      
    KSAUTOMATION_TABLE *        pNewAutomationTable = NULL;
    ULONG                       uliPath;
    ULONG                       ulcNodes;
    ULONG                       ulcbNodeDescriptor;

    //  Check for required parameters
    //
    if (!pFilterCtx || !ObjectBag)
    {
        status = STATUS_INVALID_PARAMETER;
        goto exit;
    }

    if (   !pFilterCtx->ulcPathInfo
        || !pFilterCtx->pBdaFilterTemplate
        || !pFilterCtx->pBdaFilterTemplate->pFilterDescriptor
        || !pFilterCtx->pBdaFilterTemplate->pFilterDescriptor->NodeDescriptorsCount
        || !pFilterCtx->pBdaFilterTemplate->pFilterDescriptor->NodeDescriptors
       )
    {
        goto exit;
    }

    pFilterDescriptor = pFilterCtx->pBdaFilterTemplate->pFilterDescriptor;

    //  If ulcNodeControlInfo is not zero the that array of control info
    //  structures must exist
    //
    ASSERT( pFilterCtx->argpPathInfo);
    if (!pFilterCtx->argpPathInfo)
    {
        status = STATUS_INVALID_DEVICE_STATE;
        goto exit;
    }

    //  Initial variables used to step through the list of node descriptors
    //  for the filter to which this pin type belongs.
    //
    ulcNodes = pFilterDescriptor->NodeDescriptorsCount;
    ulcbNodeDescriptor = pFilterDescriptor->NodeDescriptorSize;

    //  Step through each template node descriptor and, if it is controlled,
    //  by the given pin type, add its automation table to resulting table.
    //
    for ( uliPath = 0
        ; uliPath < pFilterCtx->ulcPathInfo
        ; uliPath++
        )
    {
        PBDA_PATH_INFO              pPathInfo;
        ULONG                       uliPathEntry;
        BOOLEAN                     fMergeNode = FALSE;

        pPathInfo = pFilterCtx->argpPathInfo[uliPath];

        //  Skip paths that don't include this pin type.
        //
        if (pPathInfo->ulInputPin == ulControllingPinType)
        {
            //  If the controlling pin is an input pin then we
            //  will start merging nodes right away and quit when
            //  we find a topology joint.
            //
            fMergeNode = TRUE;
        }
        else if (pPathInfo->ulOutputPin == ulControllingPinType)
        {
            //  If the controlling pin is an output pin then we
            //  will not merge nodes until we find a topology
            //  joint.
            //
            fMergeNode = FALSE;
        }
        else
        {
            //  The pin we're interested in isn't part of this path.
            continue;
        }

        //  Loop through each connection in the path to see if it points
        //  to a node whose automation table needs to be merged to the
        //  pin.
        //
        for ( uliPathEntry = 0
            ; uliPathEntry < pPathInfo->ulcPathEntries
            ; uliPathEntry++
            )
        {
            const KSTOPOLOGY_CONNECTION *   pConnection;
            ULONG                           uliConnection;
            const KSNODE_DESCRIPTOR *       pNodeDescriptor;
            ULONG                           uliNode;

            if (pPathInfo->rgPathEntries[uliPathEntry].fJoint)
            {
                //  Switch the merge state on a topology joint.
                //
                fMergeNode = !fMergeNode;
                if (!fMergeNode)
                {
                    //  If we were merging input side nodes then we're done
                    //
                    break;
                }
            }

            if (!fMergeNode)
            {
                continue;
            }

            //  Get the "ToNode" from this connection and, if it is not
            //  an output pin, merge its automation table.
            //
            uliConnection = pPathInfo->rgPathEntries[uliPathEntry].uliConnection;
            pConnection = &(pFilterDescriptor->Connections[uliConnection]);
            uliNode = pConnection->ToNode;
            if (   (uliNode == -1)
                || (uliNode >= pFilterDescriptor->NodeDescriptorsCount)
               )
            {
                //  This connection's "ToNode" is an output pin so
                //  skip it.
                //
                continue;
            }
    
            //  Find the Node Descriptor for the node type
            //
            pNodeDescriptor = pFilterDescriptor->NodeDescriptors;
            pNodeDescriptor = (const KSNODE_DESCRIPTOR *)
                                 (  (const BYTE *) (pNodeDescriptor)
                                  + (ulcbNodeDescriptor * uliNode)
                                 );
        
            //  Merge the nodes automation table into the resulting automation
            //  table.
            //
            //$BUGBUG - KsMergeAutomationTables should take const xxx *
            //
            if (!pOriginalAutomationTable)
            {
                pOriginalAutomationTable 
                    = (PKSAUTOMATION_TABLE) (pNodeDescriptor->AutomationTable);
            }
            else
            {
                status = KsMergeAutomationTables( 
                             &pNewAutomationTable,
                             (PKSAUTOMATION_TABLE) pOriginalAutomationTable,
                             (PKSAUTOMATION_TABLE) (pNodeDescriptor->AutomationTable),
                             ObjectBag
                             );
                if (status != STATUS_SUCCESS)
                {
                    goto exit;
                }
                ASSERT( pNewAutomationTable);
        
                pOriginalAutomationTable = pNewAutomationTable;
                pNewAutomationTable = NULL;
            }
        }

    }

exit:
    *ppNewAutomationTable = (PKSAUTOMATION_TABLE) pOriginalAutomationTable;
    return status;
}


/*
**  BdaDeletePin()
**
**      Utility function deletes a pin from the given filter instance.
**
**
**  Arguments:
**
**
**      PinType         Pin type to create.
**
**      pPinId          Id of the Pin that was created.
**
**  Returns:
**
**
** Side Effects:  none
*/

STDMETHODIMP_(NTSTATUS)
BdaDeletePin(
    PKSFILTER                   pKSFilter,
    PULONG                      pulPinId
    )
{
    NTSTATUS                status = STATUS_SUCCESS;
    PBDA_FILTER_CONTEXT     pFilterCtx;

    status = BdaGetFilterContext( pKSFilter, &pFilterCtx);

    if (status != STATUS_SUCCESS)
    {
        goto errExit;
    }

    ASSERT( pFilterCtx);

errExit:
    return status;
}


/*
**  BdaPathExists()
**
**      Utility function checks if there is a path between the input and
**      the output.
**
**
**  Arguments:
**
**      InputPinId
**
**      OutPutPinId
**
**  Returns:
**
**      TRUE            If a path exists.
**      FALSE           If no path exists.
**
** Side Effects:  none
*/

STDMETHODIMP_(BOOLEAN)
BdaPathExists(
    const KSFILTER_DESCRIPTOR * pFilterDescriptor,
    ULONG                       ulInputPinId,
    ULONG                       ulOutputPinId
    )
{
    const KSTOPOLOGY_CONNECTION *   pConnection;
    ULONG                           ulcConnections;
    ULONG                           uliConnection;

    if (   !pFilterDescriptor
        || !pFilterDescriptor->ConnectionsCount
        || !pFilterDescriptor->Connections
       )
    {
        return FALSE;
    }

    //$REVIEW - Assume only DShow style internal connections.
    //$REVIEW   (ie connections between pins with no intervening nodes)
    //
    ulcConnections = pFilterDescriptor->ConnectionsCount;
    pConnection = pFilterDescriptor->Connections;
    for (uliConnection = 0; uliConnection < ulcConnections; uliConnection++)
    {
        if (   (pConnection[uliConnection].FromNode == -1)
            && (pConnection[uliConnection].FromNodePin == ulInputPinId)
            && (pConnection[uliConnection].ToNode == -1)
            && (pConnection[uliConnection].ToNodePin == ulOutputPinId)
           )
        {
            break;
        }
    }

    return (uliConnection < ulcConnections);
}


/*
**  BdaCreateTopology()
**
**      Utility function creates the topology between two pins.
**
**
**  Arguments:
**
**
**      InputPinId
**
**      OutPutPinId
**
**  Returns:
**
**      NULL            If no valid pin pairing exists with the
**                      given input and output pins.
**
** Side Effects:  none
*/

STDMETHODIMP_(NTSTATUS)
BdaCreateTopology(
    PKSFILTER                   pKSFilter,
    ULONG                       ulInputPinId,
    ULONG                       ulOutputPinId
    )
{
    NTSTATUS                    status = STATUS_SUCCESS;
    PBDA_FILTER_CONTEXT         pFilterCtx = NULL;
    const KSFILTER_DESCRIPTOR * pFilterDesc;
    ULONG                       uliPinPair;
    ULONG                       ulcPinPairs;
    const BDA_PIN_PAIRING *     pPinPairs;
    ULONG                       ulInputPinType;
    ULONG                       ulOutputPinType;

    if (!pKSFilter)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }

    status = BdaGetFilterContext( pKSFilter, &pFilterCtx);

    if (status != STATUS_SUCCESS)
    {
        goto errExit;
    }

    ASSERT( pFilterCtx);
    if (!pFilterCtx)
    {
        status = STATUS_INVALID_DEVICE_STATE;
        goto errExit;
    }

    ASSERT( pFilterCtx->pBdaFilterTemplate);
    if (!pFilterCtx->pBdaFilterTemplate)
    {
        status = STATUS_INVALID_DEVICE_STATE;
        goto errExit;
    }

    pPinPairs = pFilterCtx->pBdaFilterTemplate->pPinPairs;
    ulcPinPairs = pFilterCtx->pBdaFilterTemplate->ulcPinPairs;

    pFilterDesc = pKSFilter->Descriptor;

    if (   !pFilterDesc
        || (ulInputPinId >= pFilterDesc->PinDescriptorsCount)
        || (ulOutputPinId >= pFilterDesc->PinDescriptorsCount)
       )
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }

    if (BdaPathExists( pFilterDesc, ulInputPinId, ulOutputPinId))
    {
        goto errExit;
    }

    //  Get the input pin type.
    //
    status = BdaPinTypeFromPinId( pFilterCtx, 
                                  ulInputPinId,
                                  &ulInputPinType
                                  );
    if (status != STATUS_SUCCESS)
    {
        goto errExit;
    }

    //  Get the output pin type.
    //
    status = BdaPinTypeFromPinId( pFilterCtx, 
                                  ulOutputPinId,
                                  &ulOutputPinType
                                  );
    if (status != STATUS_SUCCESS)
    {
        goto errExit;
    }

    //  See if there is a pin pairing to match the requested topology.
    //
    for (uliPinPair = 0; uliPinPair < ulcPinPairs; uliPinPair++)
    {
        if (   (pPinPairs[uliPinPair].ulInputPin == ulInputPinType)
            && (pPinPairs[uliPinPair].ulOutputPin == ulOutputPinType)
           )
        {
            break;
        }
    }
    if (uliPinPair >= ulcPinPairs)
    {
        status = STATUS_INVALID_DEVICE_REQUEST;
        goto errExit;
    }

    {
        KSTOPOLOGY_CONNECTION   ksConnection;

        //  Add a path between the pins to the filter descriptor
        //
        ksConnection.FromNode = -1;
        ksConnection.FromNodePin = ulInputPinId;
        ksConnection.ToNode = -1;
        ksConnection.ToNodePin = ulOutputPinId;
    
        status = KsFilterAddTopologyConnections ( pKSFilter,
                                                  1,
                                                  &ksConnection
                                                  );
    }

errExit:
    return status;
}


/*
**  BdaInitFilterFactoryContext()
**
**      Initializes a BDA Filter Factory Context based on the filter's
**      template descriptor.
**
**
**  Arguments:
**
**
**      pFilterFactoryCtx
**
**  Returns:
**
**      NULL            If no valid pin pairing exists with the
**                      given input and output pins.
**
** Side Effects:  none
*/

STDMETHODIMP_(NTSTATUS)
BdaInitFilterFactoryContext(
    PBDA_FILTER_FACTORY_CONTEXT pFilterFactoryCtx
    )
{
    NTSTATUS                status = STATUS_SUCCESS;

    ASSERT( pFilterFactoryCtx);
    if (!pFilterFactoryCtx)
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }

    if (!pFilterFactoryCtx->pBdaFilterTemplate)
    {
        goto errExit;
    }

errExit:
    return status;
}


/*
**  BdaPushNextPathHop()
**
**      Recursively pushes connections onto the connection stack until
**      (starting with the input pin of the pin pair) until either the
**      output pin is found or there are no connections that can be pushed.
**
**
**  Arguments:
**
**
**      pFilterFactoryCtx
**
**  Returns:
**
**      NULL            If no valid pin pairing exists with the
**                      given input and output pins.
**
** Side Effects:  none
*/

STDMETHODIMP_(NTSTATUS)
BdaPushNextPathHop(
    PULONG                          puliPathStack,
    PBDA_PATH_STACK_ENTRY           pPathStack,
    ULONG                           ulcConnections,
    const KSTOPOLOGY_CONNECTION *   pConnections,
    const BDA_PIN_PAIRING *         pPinPair
    )
{
    NTSTATUS                        status = STATUS_SUCCESS;
    ULONG                           ulHop;
    ULONG                           ulFromNode;
    ULONG                           ulFromNodePin;
    ULONG                           uliConnection;

    //  Determine which node we are currently finding connection to.
    //
    if (!*puliPathStack)
    {
        //  We are pushing the first hop.
        //
        ulHop = 0;

        //  Hop 0 is always the input pin
        //
        ulFromNode = -1;
        ulFromNodePin = pPinPair->ulInputPin;
    }
    else
    {
        //  We are pushing the next hop.
        //
        ulHop = pPathStack[*puliPathStack - 1].ulHop + 1;

        //  We will be looking for connections from the "ToNode" of the
        //  connection at the top of the stack.
        //
        uliConnection = pPathStack[*puliPathStack - 1].uliConnection;
        ulFromNode = pConnections[uliConnection].ToNode;
        ulFromNodePin = pConnections[uliConnection].ToNodePin;
    }

    //  GO through each connection in the filter factories connection
    //  and push any connection to ulFromNode onto the connections stack.
    //  If a connection from ulFromNode to the output pin of the given
    //  pin pair is found then we have found a complete path for the
    //  pin pair.
    //
    for ( uliConnection = 0
        ; uliConnection < ulcConnections
        ; uliConnection++
        )
    {
        ULONG           uliJoints;
        const ULONG *   pJoints;


        if (pConnections[uliConnection].FromNode != ulFromNode)
        {
            //  Connection is not from the node at the top of the stack.
            //
            continue;
        }

        if (   (pConnections[uliConnection].FromNode == -1)
            && (pConnections[uliConnection].FromNodePin != ulFromNodePin)
           )
        {
            //  The input pin is at the top of the stack and this connection
            //  is not from the input pin of the pin pair.
            //
            continue;
        }
        
        //
        //  This connection is from the node that was on top of the stack
        //  when this function was called.  Push it onto the stack.
        //

        if (*puliPathStack >= ulcConnections)
        {
            //  Stack overflow
            //  Can only occur when the BDA topology contains
            //  cirular references.
            //
            status = STATUS_INVALID_PARAMETER;
            goto errExit;
        }
        
        //  Write the connection info to the next stack entry.
        //
        pPathStack[*puliPathStack].ulHop = ulHop;
        pPathStack[*puliPathStack].uliConnection = uliConnection;
        pPathStack[*puliPathStack].fJoint = FALSE;

        //  See if this connection is also a topology joint.
        //
        for ( uliJoints = 0, pJoints = pPinPair->pTopologyJoints
            ; (uliJoints < pPinPair->ulcTopologyJoints) && pJoints
            ; uliJoints++ 
            )
        {
            if (pJoints[uliJoints] == uliConnection)
            {
                pPathStack[*puliPathStack].fJoint = TRUE;
                break;
            }
        }
        
        //  Increment the stack pointer
        //
        *puliPathStack += 1;

        //  Now that the connection has been pushed on the stack.  See if it
        //  completes a path between the input and output pin pair.
        //
        if (   (pConnections[uliConnection].ToNode == -1)
            && (pConnections[uliConnection].ToNodePin == pPinPair->ulOutputPin)
           )
        {
            //  If this connection completes the path, then complete
            //  now so that the caller will find the end of the path
            //  at the top of the stack.
            //
            break;
        }
    }

errExit:

    return status;
}


/*
**  BdaPopPathSegment()
**
**      Pops the stack back to the next path segment to try.
**
**
**  Arguments:
**
**
**  Returns:
**
**
** Side Effects:  none
*/

BdaPopPathSegment(
    PULONG                          puliPathStack,
    PBDA_PATH_STACK_ENTRY           pPathStack
    )
{
    NTSTATUS                        status = STATUS_SUCCESS;
    ULONG                           ulCurHop;
    ULONG                           ulNewHop;

    ulCurHop = pPathStack[*puliPathStack].ulHop;
    while (*puliPathStack)
    {
        *puliPathStack -= 1;

        if (!*puliPathStack)
        {
            //  Empty Stack
            //
            break;
        }

        if (pPathStack[(*puliPathStack) - 1].ulHop == ulCurHop)
        {
            //  Stop here if there is another entry on the stack at
            //  the current hop count.
            // 
            break;
        }

        //  We've popped back to a new hop count, so set the current
        //  hop count and pop off another entry.
        //
        ulCurHop = pPathStack[(*puliPathStack) - 1].ulHop;
    }
    
    return status;
}


/*
**  BdaPathInfoFromPathStack()
**
**      Builds a connection path between the input and output pins of
**      a pin pair.
**
**
**  Arguments:
**
**
**  Returns:
**
**
** Side Effects:  none
*/

STDMETHODIMP_(NTSTATUS)
BdaPathInfoFromPathStack(
    ULONG                           uliPathStack,
    PBDA_PATH_STACK_ENTRY           pPathStack,
    ULONG                           ulcConnections,
    const KSTOPOLOGY_CONNECTION *   pConnections,
    const BDA_PIN_PAIRING *         pPinPair,
    PBDA_PATH_INFO *                ppPathInfo
    )
{
    NTSTATUS            status = STATUS_SUCCESS;
    PBDA_PATH_INFO      pPathInfo = NULL;
    ULONG               uliConnection;
    ULONG               ulHop;

    ASSERT( uliPathStack);
    ASSERT( pPathStack);
    ASSERT( uliPathStack <= ulcConnections);
    ASSERT( ulcConnections);
    ASSERT( pConnections);
    ASSERT( ppPathInfo);
    ASSERT( pPinPair);

    if (   !ppPathInfo
        || !pConnections
        || !pPathStack
        || !pPinPair
        || !uliPathStack
        || !ulcConnections
        || (uliPathStack > ulcConnections)
       )
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }

    //  Make sure the connection at the top of the path stack points
    //  to the output pin of the pin pair.
    //
    uliConnection = pPathStack[uliPathStack - 1].uliConnection;
    if (   (pConnections[uliConnection].ToNode != -1)
        || (pConnections[uliConnection].ToNodePin != pPinPair->ulOutputPin)
       )
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }
    
    //  Start filling in from the node at the last hop.  If the last
    //  hop == 0 then there was only one connection between the input
    //  and output pins with no intervening nodes.
    //
    ulHop = pPathStack[uliPathStack - 1].ulHop;

    //  Allocate enough space for the node path structure and all
    //  nodes in the path.
    //
    pPathInfo = ExAllocatePool( 
                           NonPagedPool,
                             sizeof( BDA_PATH_INFO)
                           + (ulHop + 1) * sizeof( BDA_PATH_STACK_ENTRY)
                           );
    if (!pPathInfo)
    {
        status = STATUS_NO_MEMORY;
        goto errExit;
    }

    pPathInfo->ulInputPin = pPinPair->ulInputPin;
    pPathInfo->ulOutputPin = pPinPair->ulOutputPin;
    pPathInfo->ulcPathEntries = ulHop + 1;

    while (uliPathStack)
    {
        //  Enter the Connection info into the path connection list
        //
        //
        pPathInfo->rgPathEntries[ulHop] = pPathStack[uliPathStack - 1];

        //  Pop the path stack up to the top entry of the next lower hop.
        //  If exhaust the path stack then we are either done or the path
        //  stack didn't reflect a complete path from input pin to
        //  output pin.
        //
        ulHop -= 1;
        while (   uliPathStack
               && (pPathStack[uliPathStack - 1].ulHop != ulHop)
              )
        {
            uliPathStack -= 1;
        }
    }

    //  We should alway end up pointing to a connection between the input
    //  pin and the first node of the path to the output pin.
    //
    ASSERT( ulHop == -1);
    if (ulHop != -1)
    {
        status = STATUS_INVALID_DEVICE_STATE;
        goto errExit;
    }

    //  Make sure the last connection points back to the input pin.
    //
    uliConnection = pPathInfo->rgPathEntries[0].uliConnection;
    if (   (pConnections[uliConnection].FromNode != -1)
        || (pConnections[uliConnection].FromNodePin != pPinPair->ulInputPin)
       )
    {
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }

exit:
    *ppPathInfo = pPathInfo;
    pPathInfo = NULL;

    return status;

errExit:
    if (pPathInfo)
    {
        ExFreePool( pPathInfo);
        pPathInfo = NULL;
    }

    goto exit;
}


/*
**  BdaBuildPath()
**
**      Builds a connection path between the input and output pins of
**      a pin pair.
**
**
**  Arguments:
**
**
**  Returns:
**
**
** Side Effects:  none
*/

STDMETHODIMP_(NTSTATUS)
BdaBuildPath(
    ULONG                           ulcConnections,
    const KSTOPOLOGY_CONNECTION *   pConnections,
    const BDA_PIN_PAIRING *         pPinPair,
    PBDA_PATH_INFO *                ppPathInfo
    )
{
    NTSTATUS                        status = STATUS_SUCCESS;
    ULONG                           uliStackPointer;
    PBDA_PATH_STACK_ENTRY           pPathStack = NULL;
    ULONG                           ulcAttempts;
    ULONG                           uliConnection;


    //  Allocate a stack on which to put unfollowed connections to a path.
    //
    pPathStack = ExAllocatePool( 
                               NonPagedPool, 
                               ulcConnections * sizeof( BDA_PATH_STACK_ENTRY)
                               );
    if (!pPathStack)
    {
        status = STATUS_NO_MEMORY;
        goto errExit;
    }


    //  Initialize the unfollowed connection stack
    //
    uliStackPointer = 0;

    //  Build a path stack by pushing each connection that connects from
    //  the previous hop.
    //
    //  It isn't possible to attempt to push the next hop more times than
    //  there are connections.  If this happens then there is an illegal
    //  circular path in the connection list.
    //
    for (ulcAttempts = 0; ulcAttempts < ulcConnections; ulcAttempts++)
    {
        ULONG   uliPrevStackPointer;

        uliPrevStackPointer = uliStackPointer;

        status = BdaPushNextPathHop( &uliStackPointer, 
                                     pPathStack, 
                                     ulcConnections,
                                     pConnections,
                                     pPinPair
                            );

        if (!uliStackPointer)
        {
            //  If the stack is empty at this point then there is
            //  no path from the input pin to the output pin.
            //
            break;
        }

        //  See if the connection at the top of the stack connects to
        //  the output pin of the pin pair.
        //
        uliConnection = pPathStack[uliStackPointer - 1].uliConnection;
        if (   (pConnections[uliConnection].ToNode == -1)
            && (pConnections[uliConnection].ToNodePin == pPinPair->ulOutputPin)
           )
        {
            //  A path from the input pin to the output pin has been
            //  located.
            //
            break;
        }

        //  If no hop could be pushed onto the connnection at the top of
        //  the stack then it is a dead end.
        //
        if (uliStackPointer <= uliPrevStackPointer)
        {
            //  Pop connections from the stack until we reach a viable
            //  (new) candidate to attempt a path from.
            //
            BdaPopPathSegment( &uliStackPointer, pPathStack);

            if (!uliStackPointer)
            {
                //  If the stack is empty at this point then there is
                //  no path from the input pin to the output pin.
                //
                break;
            }
        }
    }

    if (!uliStackPointer || (ulcAttempts >= ulcConnections))
    {
        //  Either there is no path from the input pin to the output pin
        //  or there is an illegal circular path in the connection list
        //
        status = STATUS_INVALID_PARAMETER;
        goto errExit;
    }

    //  Create a BDA node path structure from the Path Stack
    //
    //$REVIEW - Should we allow more than one path per pin pair
    //
    status = BdaPathInfoFromPathStack( uliStackPointer,
                                       pPathStack,
                                       ulcConnections,
                                       pConnections,
                                       pPinPair,
                                       ppPathInfo
                                       );

errExit:
    if (pPathStack)
    {
        ExFreePool( pPathStack);
        pPathStack = NULL;
    }

    return status;
}


/*
**  BdaCreateTemplatePaths()
**
**      Creates a list of all possible paths through the template filter.
**      Determines the controlling pin type for each node type in the
**      template filter.
**
**
**  Arguments:
**
**
**      pFilterFactoryCtx
**
**  Returns:
**
**      NULL            If no valid pin pairing exists with the
**                      given input and output pins.
**
** Side Effects:  none
*/

STDMETHODIMP_(NTSTATUS)
BdaCreateTemplatePaths(
    const BDA_FILTER_TEMPLATE *     pBdaFilterTemplate,
    PULONG                          pulcPathInfo,
    PBDA_PATH_INFO **               pargpPathInfo
    )
{
    NTSTATUS                        status = STATUS_SUCCESS;
    const BDA_FILTER_TEMPLATE *     pFilterTemplate;
    ULONG                           uliPinPair;
    ULONG                           ulcPinPairs;
    const BDA_PIN_PAIRING *         pPinPairs;
    ULONG                           ulcConnections;
    const KSTOPOLOGY_CONNECTION *   pConnections;
    PBDA_PATH_INFO *                argpPathInfo = NULL;

    ASSERT( pBdaFilterTemplate);
    ASSERT( pBdaFilterTemplate->pFilterDescriptor);
    ASSERT( pBdaFilterTemplate->ulcPinPairs);

    if (   !pBdaFilterTemplate
        || !pBdaFilterTemplate->pFilterDescriptor
        || !pBdaFilterTemplate->ulcPinPairs
       )
    {
        goto errExit;
    }

    if (   !pBdaFilterTemplate->pFilterDescriptor->ConnectionsCount
        || !pBdaFilterTemplate->pFilterDescriptor->Connections
        || !pBdaFilterTemplate->pPinPairs
       )
    {
        status = STATUS_INVALID_DEVICE_STATE;
        goto errExit;
    }

    ulcPinPairs = pBdaFilterTemplate->ulcPinPairs;
    pPinPairs = pBdaFilterTemplate->pPinPairs;
    ulcConnections = pBdaFilterTemplate->pFilterDescriptor->ConnectionsCount;
    pConnections = pBdaFilterTemplate->pFilterDescriptor->Connections;


    //  Allocate the node path list (one entry for each pin pairing).
    //
    //$REVIEW - Should we allow more than one path per pin pair
    //
    argpPathInfo = ExAllocatePool(
                     NonPagedPool,
                     ulcPinPairs * sizeof( PBDA_PATH_INFO)
                     );
    if (!argpPathInfo)
    {
        status = STATUS_NO_MEMORY;
        goto errExit;
    }


    for (uliPinPair = 0; uliPinPair < ulcPinPairs; uliPinPair++)
    {
        status = BdaBuildPath(
                    ulcConnections,
                    pConnections,
                    &(pPinPairs[uliPinPair]),
                    &(argpPathInfo[uliPinPair])
                    );
        if (status != STATUS_SUCCESS)
        {
            goto errExit;
        }
    }

    *pulcPathInfo = ulcPinPairs;
    *pargpPathInfo = argpPathInfo;
    argpPathInfo = NULL;


errExit:
    if (argpPathInfo)
    {
        ExFreePool( argpPathInfo);
        argpPathInfo = NULL;
    }

    return status;
}