/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:

    cxpnp.c

Abstract:

    PnP handling code for the Cluster Network Driver.

Author:

    Mike Massa (mikemas)           March 21, 1998

Revision History:

    Who         When        What
    --------    --------    ----------------------------------------------
    mikemas     03-22-98    created

Notes:

--*/

#include "precomp.h"

#include <ntddk.h>
#include <wmistr.h>
#include <ndisguid.h>
#include <ntddndis.h>
#include <ntpnpapi.h>
#include <zwapi.h>

#pragma hdrstop
#include "cxpnp.tmh"

//
// Local data structures
//
typedef struct _CNP_WMI_RECONNECT_WORKER_CONTEXT {
    PIO_WORKITEM  WorkItem;
    CL_NETWORK_ID NetworkId;
} CNP_WMI_RECONNECT_WORKER_CONTEXT, *PCNP_WMI_RECONNECT_WORKER_CONTEXT;

//
// WMI Data
//
PERESOURCE CnpWmiNdisMediaStatusResource = NULL;
PVOID      CnpWmiNdisMediaStatusConnectObject = NULL;
PVOID      CnpWmiNdisMediaStatusDisconnectObject = NULL;
HANDLE     CnpIpMediaSenseFileHandle = NULL;
PIRP       CnpIpDisableMediaSenseIrp = NULL;
PKEVENT    CnpIpDisableMediaSenseEvent = NULL;


//
// Local prototypes
//
NTSTATUS
CnpWmiPnpDisableMediaSenseCompletion(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP           Irp,
    IN PVOID          Context
    );

VOID
CnpWmiNdisMediaStatusConnectCallback(
    IN PVOID             Wnode,
    IN PVOID             Context
    );

VOID
CnpWmiNdisMediaStatusDisconnectCallback(
    IN PVOID             Wnode,
    IN PVOID             Context
    );

VOID
CnpReconnectLocalInterfaceWrapper(
    IN PDEVICE_OBJECT DeviceObject,
    IN PVOID          Context
    );

VOID
CnpDisconnectLocalInterface(
    PCNP_INTERFACE Interface,
    PCNP_NETWORK   Network
    );


#ifdef ALLOC_PRAGMA

#pragma alloc_text(INIT, CxWmiPnpLoad)
#pragma alloc_text(PAGE, CxWmiPnpUnload)
#pragma alloc_text(PAGE, CxWmiPnpInitialize)
#pragma alloc_text(PAGE, CxWmiPnpShutdown)

#endif // ALLOC_PRAGMA


//
// Exported Routines
//
VOID
CxTdiAddAddressHandler(
    IN PTA_ADDRESS       TaAddress,
    IN PUNICODE_STRING   DeviceName,
    IN PTDI_PNP_CONTEXT  Context
    )
{
    if (TaAddress->AddressType == TDI_ADDRESS_TYPE_IP) {
        NTSTATUS          status;
        PTDI_ADDRESS_IP   tdiAddressIp = (PTDI_ADDRESS_IP)
                                         &(TaAddress->Address[0]);


        IF_CNDBG(CN_DEBUG_CONFIG) {
            CNPRINT((
                "[CX] Processing PnP add event for IP address %lx\n",
                tdiAddressIp->in_addr
                ));
        }

        //
        // Ensure that this is a valid address, and that it is not one
        // that we brought online for a cluster ip address resource.
        //
        if (tdiAddressIp->in_addr != 0) {
            if (!IpaIsAddressRegistered(tdiAddressIp->in_addr)) {
                IF_CNDBG(CN_DEBUG_CONFIG) {
                    CNPRINT((
                        "[CX] Issuing address add event to cluster svc for IP address %lx\n",
                        tdiAddressIp->in_addr
                        ));
                }
                CnIssueEvent(
                    ClusnetEventAddAddress,
                    0,
                    (CL_NETWORK_ID) tdiAddressIp->in_addr
                    );
            }
            else {
                IF_CNDBG(CN_DEBUG_CONFIG) {
                    CNPRINT((
                        "[CX] PnP add event is for an IP address resource, skip.\n"
                        ));
                }
            }
        }
    }

    return;

} // CxTdiAddAddressHandler


VOID
CxTdiDelAddressHandler(
    IN PTA_ADDRESS       TaAddress,
    IN PUNICODE_STRING   DeviceName,
    IN PTDI_PNP_CONTEXT  Context
    )
{


    if (TaAddress->AddressType == TDI_ADDRESS_TYPE_IP) {
        NTSTATUS           status;
        PCNP_INTERFACE     interface;
        PCNP_NETWORK       network;
        PLIST_ENTRY        entry;
        PTA_IP_ADDRESS     taIpAddress;
        CN_IRQL            nodeTableIrql;
        CL_NODE_ID         i;
        PTDI_ADDRESS_IP    tdiAddressIp = (PTDI_ADDRESS_IP)
                                          &(TaAddress->Address[0]);

        IF_CNDBG(CN_DEBUG_CONFIG) {
            CNPRINT((
                "[CX] Processing PnP delete event for IP address %lx.\n",
                tdiAddressIp->in_addr
                ));
        }

        if (tdiAddressIp->in_addr != 0) {
            //
            // Figure out if this is the address for one of this node's
            // registered interfaces.
            //
            CnAcquireLock(&CnpNodeTableLock, &nodeTableIrql);

            if (CnpLocalNode != NULL) {
                CnAcquireLockAtDpc(&(CnpLocalNode->Lock));
                CnReleaseLockFromDpc(&CnpNodeTableLock);
                CnpLocalNode->Irql = nodeTableIrql;

                network = NULL;

                for (entry = CnpLocalNode->InterfaceList.Flink;
                     entry != &(CnpLocalNode->InterfaceList);
                     entry = entry->Flink
                    )
                {
                    interface = CONTAINING_RECORD(
                                    entry,
                                    CNP_INTERFACE,
                                    NodeLinkage
                                    );

                    taIpAddress = (PTA_IP_ADDRESS) &(interface->TdiAddress);

                    if (taIpAddress->Address[0].Address[0].in_addr ==
                        tdiAddressIp->in_addr
                       )
                    {
                        //
                        // Found the local interface corresponding to this
                        // address. Be proactive - destroy the corresponding
                        // network now.
                        //
                        network = interface->Network;

                        CnAcquireLockAtDpc(&CnpNetworkListLock);
                        CnAcquireLockAtDpc(&(network->Lock));
                        CnReleaseLockFromDpc(&(CnpLocalNode->Lock));
                        network->Irql = DISPATCH_LEVEL;

                        IF_CNDBG(CN_DEBUG_CONFIG) {
                            CNPRINT((
                                "[CX] Deleting network ID %u after PnP "
                                "delete event for IP address %lx.\n",
                                network->Id, tdiAddressIp->in_addr
                                ));
                        }

                        CnpDeleteNetwork(network, nodeTableIrql);

                        //
                        // Both locks were released.
                        //
                        break;
                    }
                }

                if (network == NULL) {
                    CnReleaseLock(&(CnpLocalNode->Lock), CnpLocalNode->Irql);
                }

                //
                // Post an event to the service.
                //
                CnIssueEvent(
                    ClusnetEventDelAddress,
                    0,
                    (CL_NETWORK_ID) tdiAddressIp->in_addr
                    );
            }
            else {
                CnReleaseLock(&CnpNodeTableLock, nodeTableIrql);
            }
        }
    }

    return;

} // CxTdiDelAddressHandler


NTSTATUS
CxWmiPnpLoad(
    VOID
    )
/*++

Notes:

    Called when clusnet driver is loaded.
    
--*/    
{
    PDEVICE_OBJECT     ipDeviceObject = NULL;
    PFILE_OBJECT       ipFileObject = NULL;
    PIO_STACK_LOCATION irpSp;
    NTSTATUS           status;

    //
    // Allocate a synchronization resource.
    //
    CnpWmiNdisMediaStatusResource = CnAllocatePool(sizeof(ERESOURCE));

    if (CnpWmiNdisMediaStatusResource == NULL) {
        return(STATUS_INSUFFICIENT_RESOURCES);
    }

    status = ExInitializeResourceLite(CnpWmiNdisMediaStatusResource);

    if (!NT_SUCCESS(status)) {
        return(status);
    }

    //
    // Get a handle to the IP device object to disable media sense
    //
    status = CnpOpenDevice(
                 DD_IP_DEVICE_NAME,
                 &CnpIpMediaSenseFileHandle
                 );
    if (!NT_SUCCESS(status)) {
        IF_CNDBG(CN_DEBUG_INIT) {
            CNPRINT(("[CX] Failed to open IP device to "
                     "disable media sense, status %lx\n", 
                     status));
        }
        return(status);
    }

    //
    // Disable IP media sense. This works by submitting an
    // IOCTL_IP_DISABLE_MEDIA_SENSE_REQUEST IRP. The IRP
    // will pend until we cancel it (re-enabling media sense).
    //
    CnpIpDisableMediaSenseEvent = CnAllocatePool(sizeof(KEVENT));

    if (CnpIpDisableMediaSenseEvent != NULL) {

        KeInitializeEvent(
            CnpIpDisableMediaSenseEvent,
            SynchronizationEvent,
            FALSE
            );

        //
        // Reference the IP file object and get the device object
        //
        status = ObReferenceObjectByHandle(
                     CnpIpMediaSenseFileHandle,
                     0,
                     NULL,
                     KernelMode,
                     &ipFileObject,
                     NULL
                     );

        if (NT_SUCCESS(status)) {

            ipDeviceObject = IoGetRelatedDeviceObject(ipFileObject);

            //
            // File object reference is no longer needed
            // because the handle is still open.
            //
            ObDereferenceObject(ipFileObject);

            CnpIpDisableMediaSenseIrp = IoAllocateIrp(
                                            ipDeviceObject->StackSize,
                                            FALSE
                                            );

            if (CnpIpDisableMediaSenseIrp != NULL) {

                irpSp = IoGetNextIrpStackLocation(CnpIpDisableMediaSenseIrp);

                irpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
                irpSp->Parameters.DeviceIoControl.IoControlCode
                    = IOCTL_IP_DISABLE_MEDIA_SENSE_REQUEST;
                irpSp->DeviceObject = ipDeviceObject;
                irpSp->FileObject = ipFileObject;

                IoSetCompletionRoutine(
                    CnpIpDisableMediaSenseIrp,
                    CnpWmiPnpDisableMediaSenseCompletion,
                    NULL,
                    TRUE,
                    TRUE,
                    TRUE
                    );

                status = IoCallDriver(
                             ipDeviceObject, 
                             CnpIpDisableMediaSenseIrp
                             );

                if (status != STATUS_PENDING) {
                    IF_CNDBG(CN_DEBUG_INIT) {
                        CNPRINT(("[CX] Failed to disable IP media "
                                 "sense, status %lx\n", status));
                    }
                    KeWaitForSingleObject(
                        CnpIpDisableMediaSenseEvent,
                        Executive,
                        KernelMode,
                        FALSE,
                        NULL
                        );
                    CnFreePool(CnpIpDisableMediaSenseEvent);
                    CnpIpDisableMediaSenseEvent = NULL;
                    IoFreeIrp(CnpIpDisableMediaSenseIrp);
                    CnpIpDisableMediaSenseIrp = NULL;

                    //
                    // Cannot risk simply returning status
                    // because we need the driver load to
                    // fail.
                    //
                    if (NT_SUCCESS(status)) {
                        status = STATUS_UNSUCCESSFUL;
                    }

                } else {

                    //
                    // Need to return STATUS_SUCCESS so that
                    // the driver load will not fail.
                    //
                    status = STATUS_SUCCESS;

                    IF_CNDBG(CN_DEBUG_INIT) {
                        CNPRINT(("[CX] IP media sense disabled.\n"));
                    }

                    CnTrace(
                        CXPNP, CxWmiPnpIPMediaSenseDisabled,
                        "[CXPNP] IP media sense disabled.\n"
                        );
                }

            } else {

                IF_CNDBG(CN_DEBUG_INIT) {
                    CNPRINT(("[CX] Failed to allocate IP media sense "
                             "disable IRP.\n"));
                }
                CnFreePool(CnpIpDisableMediaSenseEvent);
                CnpIpDisableMediaSenseEvent = NULL;
                status = STATUS_INSUFFICIENT_RESOURCES;
            }

        } else {
            IF_CNDBG(CN_DEBUG_INIT) {
                CNPRINT(("[CX] Failed to reference IP device "
                         "file handle, status %lx\n", status));
            }
            CnFreePool(CnpIpDisableMediaSenseEvent);
            CnpIpDisableMediaSenseEvent = NULL;
        }

    } else {

        IF_CNDBG(CN_DEBUG_INIT) {
            CNPRINT(("[CX] Failed to allocate IP media sense "
                     "disable event.\n"));
        }
        status = STATUS_INSUFFICIENT_RESOURCES;
    }

    return(status);

}  // CxWmiPnpLoad


VOID
CxWmiPnpUnload(
    VOID
    )
/*++

Notes:

    Called when clusnet driver is unloaded.
    
--*/    
{
    CnAssert(CnpWmiNdisMediaStatusConnectObject == NULL);
    CnAssert(CnpWmiNdisMediaStatusDisconnectObject == NULL);

    //
    // Re-enable IP media sense. This works by cancelling our
    // IOCTL_IP_DISABLE_MEDIA_SENSE_REQUEST IRP, which should
    // still be pending.
    //
    if (CnpIpDisableMediaSenseIrp != NULL) {
        
        if (!IoCancelIrp(CnpIpDisableMediaSenseIrp)) {

            //
            // Our disable media sense IRP could not be cancelled. This
            // probably means that it was completed because somebody
            // else submitted a media sense enable request. 
            //
            CnTrace(
                CXPNP, CnpWmiPnpDisableMediaSenseCompletionUnexpected,
                "[CXPNP] IP media sense re-enabled unexpectedly.\n"
                );

        } else {

            //
            // Irp was cancelled, and media sense is disabled as
            // expected.
            //
            CnTrace(
                CXPNP, CnpWmiPnpDisableMediaSenseCompletion,
                "[CXPNP] IP media sense re-enabled.\n"
                );
        }

        //
        // Regardless of who re-enabled media sense, we need to free
        // the media sense IRP and event. First we wait on the event,
        // which is signalled in our completion routine.
        //
        KeWaitForSingleObject(
            CnpIpDisableMediaSenseEvent,
            Executive,
            KernelMode,
            FALSE,
            NULL
            );

        CnFreePool(CnpIpDisableMediaSenseEvent);
        CnpIpDisableMediaSenseEvent = NULL;

        IoFreeIrp(CnpIpDisableMediaSenseIrp);
        CnpIpDisableMediaSenseIrp = NULL;
    } 

    CnAssert(CnpIpDisableMediaSenseIrp == NULL);

    if (CnpIpMediaSenseFileHandle != NULL) {
        ZwClose(CnpIpMediaSenseFileHandle);
        CnpIpMediaSenseFileHandle = NULL;
    }

    if (CnpWmiNdisMediaStatusResource != NULL) {
        ExDeleteResourceLite(CnpWmiNdisMediaStatusResource);
        CnFreePool(CnpWmiNdisMediaStatusResource); 
        CnpWmiNdisMediaStatusResource = NULL;
    }

}  // CxWmiPnpUnload


NTSTATUS
CxWmiPnpInitialize(
    VOID
    )
/*++

Notes:

    Called in response to initialize ioctl.
    
--*/
{
    NTSTATUS           status = STATUS_SUCCESS;
    BOOLEAN            acquired = FALSE;
    GUID               wmiGuid;

    PAGED_CODE();

    acquired = CnAcquireResourceExclusive(
                   CnpWmiNdisMediaStatusResource,
                   TRUE
                   );
    
    CnAssert(acquired == TRUE);

    //
    // Register WMI callbacks for NDIS media status events
    //

    if (CnpWmiNdisMediaStatusConnectObject == NULL) {

        wmiGuid = GUID_NDIS_STATUS_MEDIA_CONNECT;
        status = IoWMIOpenBlock(
                     &wmiGuid,
                     WMIGUID_NOTIFICATION,
                     &CnpWmiNdisMediaStatusConnectObject
                     );
        if (!NT_SUCCESS(status)) {
            CNPRINT((
                "[CX] Unable to open WMI NDIS status media connect "
                "datablock, status %lx\n",
                status
                ));
            CnpWmiNdisMediaStatusConnectObject = NULL;
            goto error_exit;
        }

        status = IoWMISetNotificationCallback(
                     CnpWmiNdisMediaStatusConnectObject,
                     CnpWmiNdisMediaStatusConnectCallback,
                     NULL
                     );
        if (!NT_SUCCESS(status)) {
            CNPRINT((
                "[CX] Unable to register WMI NDIS status media connect "
                "callback, status %lx\n",
                status
                ));
            goto error_exit;
        }

    }

    if (CnpWmiNdisMediaStatusDisconnectObject == NULL) {

        wmiGuid = GUID_NDIS_STATUS_MEDIA_DISCONNECT;
        status = IoWMIOpenBlock(
                     &wmiGuid,
                     WMIGUID_NOTIFICATION,
                     &CnpWmiNdisMediaStatusDisconnectObject
                     );
        if (!NT_SUCCESS(status)) {
            CNPRINT((
                "[CX] Unable to open WMI NDIS status media disconnect "
                "datablock, status %lx\n",
                status
                ));
            CnpWmiNdisMediaStatusDisconnectObject = NULL;
            goto error_exit;
        }

        status = IoWMISetNotificationCallback(
                     CnpWmiNdisMediaStatusDisconnectObject,
                     CnpWmiNdisMediaStatusDisconnectCallback,
                     NULL
                     );
        if (!NT_SUCCESS(status)) {
            CNPRINT((
                "[CX] Unable to register WMI NDIS status media disconnect "
                "callback, status %lx\n",
                status
                ));
            goto error_exit;
        }
    }

    goto release_exit;

error_exit:
    
    if (CnpWmiNdisMediaStatusConnectObject != NULL) {
        ObDereferenceObject(CnpWmiNdisMediaStatusConnectObject);
        CnpWmiNdisMediaStatusConnectObject = NULL;
    }

    if (CnpWmiNdisMediaStatusDisconnectObject != NULL) {
        ObDereferenceObject(CnpWmiNdisMediaStatusDisconnectObject);
        CnpWmiNdisMediaStatusDisconnectObject = NULL;
    }

release_exit:
    //
    // Release resource
    //
    if (acquired) {
        CnReleaseResourceForThread(
            CnpWmiNdisMediaStatusResource,
            (ERESOURCE_THREAD) PsGetCurrentThread()
            );
    }

    return(status);
    
}  // CxWmiPnpInitialize


VOID
CxWmiPnpShutdown(
    VOID
    )
/*++

Notes:

    Called in response to clusnet shutdown.
    
--*/
{
    BOOLEAN  acquired = FALSE;

    PAGED_CODE();

    acquired = CnAcquireResourceExclusive(
                   CnpWmiNdisMediaStatusResource,
                   TRUE
                   );
    
    CnAssert(acquired == TRUE);

    if (CnpWmiNdisMediaStatusConnectObject != NULL) {
        ObDereferenceObject(CnpWmiNdisMediaStatusConnectObject);
        CnpWmiNdisMediaStatusConnectObject = NULL;
    }

    if (CnpWmiNdisMediaStatusDisconnectObject != NULL) {
        ObDereferenceObject(CnpWmiNdisMediaStatusDisconnectObject);
        CnpWmiNdisMediaStatusDisconnectObject = NULL;
    }

    //
    // Release resource
    //
    if (acquired) {
        CnReleaseResourceForThread(
            CnpWmiNdisMediaStatusResource,
            (ERESOURCE_THREAD) PsGetCurrentThread()
            );
    }
    
    return;

}  // CxWmiPnpShutdown


VOID
CxReconnectLocalInterface(
    IN CL_NETWORK_ID NetworkId
    )
/**

Routine Description:

    Queues a worker thread to set the local interface
    associated with NetworkId to connected. Called when
    a heartbeat is received over a network that is marked
    locally disconnected.
    
Arguments:

    NetworkId - network ID of network to be reconnected
    
Return value:

    None
    
Notes:

    Can fail without reporting an error if either
    allocation fails.

--*/
{
    PCNP_WMI_RECONNECT_WORKER_CONTEXT context;
    
    context = CnAllocatePool(sizeof(CNP_WMI_RECONNECT_WORKER_CONTEXT));

    if (context != NULL) {
        
        context->WorkItem = IoAllocateWorkItem(CnDeviceObject);

        if (context->WorkItem != NULL) {

            context->NetworkId = NetworkId;

            CnTrace(
                CXPNP, CxReconnectLocalInterface,
                "[CXPNP] Queueing worker thread to reconnect local "
                "interface for network ID %u.\n",
                NetworkId // LOGULONG
                );

            IoQueueWorkItem(
                context->WorkItem, 
                CnpReconnectLocalInterfaceWrapper, 
                DelayedWorkQueue,
                context
                );
        
        } else {

            CnFreePool(context);
        }
    }

    return;
}


VOID
CxQueryMediaStatus(
    IN  HANDLE            AdapterDeviceHandle,
    IN  CL_NETWORK_ID     NetworkId,
    OUT PULONG            MediaStatus
    )
/**

Routine Description:

    Queries the status of the adapter device. Used to 
    determine whether a local interface is initially
    connected or disconnected.

Arguments:

    AdapterHandle - adapter device object handle
    NetworkId - network ID of adapter to be queried
    
Return value:

    None
    
Notes:

    NDIS query formation modeled after ndis\lib\ndisapi.c

--*/
{
    BOOLEAN                      acquired = FALSE;
    NTSTATUS                     status;

    CnVerifyCpuLockMask(
        0,                  // Required
        0xFFFFFFFF,         // Forbidden
        0                   // Maximum
        );

    //
    // Set default
    //
    *MediaStatus = NdisMediaStateDisconnected;

    //
    // Acquire resource
    //
    acquired = CnAcquireResourceExclusive(
                   CnpWmiNdisMediaStatusResource,
                   TRUE
                   );
    
    CnAssert(acquired == TRUE);

    if (AdapterDeviceHandle != NULL) {
        
        //
        // Construct NDIS statistics query
        //
        
        NDIS_OID statsOidList[] =
        {
            OID_GEN_MEDIA_CONNECT_STATUS // | NDIS_OID_PRIVATE
        };
        UCHAR                  statsBuf[
                                   FIELD_OFFSET(NDIS_STATISTICS_VALUE, Data)
                                   + sizeof(LARGE_INTEGER)
                                   ];
        PNDIS_STATISTICS_VALUE pStatsBuf;
        LARGE_INTEGER          value;

        IF_CNDBG( CN_DEBUG_CONFIG ) {
            CNPRINT((
                "[CXPNP] Querying NDIS for local adapter "
                "on network %u (handle %p).\n",
                NetworkId,
                AdapterDeviceHandle
                ));
        }

        pStatsBuf = (PNDIS_STATISTICS_VALUE) &statsBuf[0];
        status = CnpZwDeviceControl(
                     AdapterDeviceHandle,
                     IOCTL_NDIS_QUERY_SELECTED_STATS,
                     statsOidList,
                     sizeof(statsOidList),
                     pStatsBuf,
                     sizeof(statsBuf)
                     );
        
        IF_CNDBG( CN_DEBUG_CONFIG ) {
            CNPRINT((
                "[CXPNP] NDIS query for local adapter "
                "on network %u returned status %lx.\n",
                NetworkId,
                status
                ));
        }

        if (pStatsBuf->DataLength == sizeof(LARGE_INTEGER)) {
            value.QuadPart = *(PULONGLONG)(&pStatsBuf->Data[0]);
        } else {
            value.LowPart = *(PULONG)(&pStatsBuf->Data[0]);
        }
        
        *MediaStatus = value.LowPart; // NdisMediaState{Disc|C}onnected
    
        IF_CNDBG( CN_DEBUG_CONFIG ) {
            CNPRINT((
                "[CXPNP] NDIS query for local adapter "
                "on network %u returned media status %lx.\n",
                NetworkId,
                *MediaStatus
                ));
        }

        CnTrace(
            CXPNP, CxQueryMediaStatus,
            "[CXPNP] Found media status %u for local network ID %u.\n",
            *MediaStatus, // LOGULONG
            NetworkId // LOGULONG
            );
    }

    //
    // If the media status is disconnected, we must disconnect the
    // local interface and network.
    //
    if (*MediaStatus == NdisMediaStateDisconnected) {

        PCNP_NETWORK                      network = NULL;
        PCNP_INTERFACE                    interface = NULL;
        CN_IRQL                           nodeTableIrql;
        PLIST_ENTRY                       entry;

        CnAcquireLock(&CnpNodeTableLock, &nodeTableIrql);

        if (CnpLocalNode != NULL) {
            CnAcquireLockAtDpc(&(CnpLocalNode->Lock));
            CnReleaseLockFromDpc(&CnpNodeTableLock);
            CnpLocalNode->Irql = nodeTableIrql;

            network = CnpFindNetwork(NetworkId);

            if (network != NULL) {

                //
                // Only go through the disconnect if the network
                // is currently marked as locally connected.
                // It is possible that we have already received
                // and processed a WMI disconnect event.
                //
                if (!CnpIsNetworkLocalDisconn(network)) {

                    for (entry = CnpLocalNode->InterfaceList.Flink;
                         entry != &(CnpLocalNode->InterfaceList);
                         entry = entry->Flink
                        )
                    {
                        interface = CONTAINING_RECORD(
                                        entry,
                                        CNP_INTERFACE,
                                        NodeLinkage
                                        );

                        if (interface->Network == network) {

                            CnpDisconnectLocalInterface(
                                interface,
                                network
                                );

                            //
                            // Both node and network locks
                            // were released.
                            //

                            break;

                        } else {
                            interface = NULL;
                        }
                    }

                } else {

                    CnTrace(
                        CXPNP, CxQueryMediaStatusDisconnectRedundant,
                        "[CXPNP] Network ID %u is already disconnected; "
                        "aborting disconnect.\n",
                        network->Id // LOGULONG
                        );
                }

                if (interface == NULL) {
                    CnReleaseLock(&(network->Lock), network->Irql);
                }
            }

            if (interface == NULL) {
                CnReleaseLock(&(CnpLocalNode->Lock), CnpLocalNode->Irql);
            }
        } else {
            CnReleaseLock(&CnpNodeTableLock, nodeTableIrql);
        }
    }
    
    //
    // Release resource
    //
    if (acquired) {
        CnReleaseResourceForThread(
            CnpWmiNdisMediaStatusResource,
            (ERESOURCE_THREAD) PsGetCurrentThread()
            );
    }
    
    CnVerifyCpuLockMask(
        0,                  // Required
        0xFFFFFFFF,         // Forbidden
        0                   // Maximum
        );

    return;

}  // CxQueryMediaStatus


//
// Local Routines
//
NTSTATUS
CnpWmiPnpDisableMediaSenseCompletion(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP           Irp,
    IN PVOID          Context
    )
{
    //
    // Irp is always freed by our disable routine to prevent a race
    // condition where we don't know if we have called IoCancelIrp
    // yet or not.
    //
    KeSetEvent(CnpIpDisableMediaSenseEvent, IO_NO_INCREMENT, FALSE);

    return(STATUS_MORE_PROCESSING_REQUIRED);

}  // CnpWmiPnpDisableMediaSenseCompletion


VOID
CnpWmiPnpUpdateCurrentInterface(
    IN  PCNP_INTERFACE   UpdateInterface
    )
/*++

Routine Description:

    Updates the CurrentInterface for interfaces after the local
    interface is connected or disconnected. Called in response 
    to WMI NDIS media status events.

Arguments:

    Interface - A pointer to the interface on which to operate.

Return Value:

    None.

Notes:

    Called with associated node and network locks held.
    Returns with network lock released.

    Conforms to calling convention for PCNP_INTERFACE_UPDATE_ROUTINE.

--*/
{
    PCNP_NODE node = UpdateInterface->Node;

    CnVerifyCpuLockMask(
        (CNP_NODE_OBJECT_LOCK | CNP_NETWORK_OBJECT_LOCK),   // Required
        0,                                                  // Forbidden
        CNP_NETWORK_OBJECT_LOCK_MAX                         // Maximum
        );

    //
    // We don't really need the network lock.  It's just part of
    // the calling convention.
    //
    CnReleaseLockFromDpc(&(UpdateInterface->Network->Lock));

    CnpUpdateNodeCurrentInterface(node);

    if ( (node->CurrentInterface == NULL)
         ||
         ( node->CurrentInterface->State <
           ClusnetInterfaceStateOnlinePending
         )
       )
    {
        //
        // This node is now unreachable.
        //
        CnpDeclareNodeUnreachable(node);
    }
    
    CnVerifyCpuLockMask(
        (CNP_NODE_OBJECT_LOCK),   // Required
        0,                        // Forbidden
        CNP_NODE_OBJECT_LOCK_MAX  // Maximum
        );

    return;

}  // CnpWmiPnpUpdateCurrentInterface


VOID
CnpReconnectLocalInterface(
    PCNP_INTERFACE Interface,
    PCNP_NETWORK   Network
    )
/*++

Routine Description:

    Changes a local interface from being disconnected to
    connected. Called in response to a WMI NDIS media status
    connect event or a heartbeat received on a disconnected
    interface.
   
Arguments:

    Interface - local interface that is reconnected
    
    Network - network associated with Interface
    
Return value:

    None
    
Notes:

    Called with CnpWmiNdisMediaStatusResource, local node lock,
    and Network lock held.
    
    Returns with CnpWmiNdisMediaStatusResource held but neither
    lock held.
    
--*/    
{
    CnVerifyCpuLockMask(
        (CNP_NODE_OBJECT_LOCK | CNP_NETWORK_OBJECT_LOCK),   // Required
        0,                                                  // Forbidden
        CNP_NETWORK_OBJECT_LOCK_MAX                         // Maximum
        );

    CnTrace(
        CXPNP, CnpReconnectLocalInterface,
        "[CXPNP] Reconnecting local interface for "
        "network ID %u.\n",
        Network->Id // LOGULONG
        );

    //
    // Clear the local disconnect flag in the network
    // object
    //
    Network->Flags &= ~CNP_NET_FLAG_LOCALDISCONN;

    //
    // Reference the network so it can't go away while we
    // reprioritize the associated interfaces.
    //
    CnpReferenceNetwork(Network);

    //
    // Bring the interface online. This call releases the
    // network lock.
    //
    CnpOnlineInterface(Interface);

    //
    // Release the node lock before walking the interfaces
    // on the network.
    //
    CnReleaseLock(&(CnpLocalNode->Lock), CnpLocalNode->Irql);

    //
    // Update the CurrentInterface for the other
    // nodes in the cluster to reflect the connected
    // status of the local interface.
    //
    CnpWalkInterfacesOnNetwork(
        Network, 
        CnpWmiPnpUpdateCurrentInterface
        );

    //
    // Issue InterfaceUp event to the cluster
    // service.
    //
    CnTrace(
        CXPNP, CxWmiNdisReconnectIssueEvent,
        "[CXPNP] Issuing InterfaceUp event "
        "for node %u on net %u, previous I/F state = %!ifstate!.",
        Interface->Node->Id, // LOGULONG
        Interface->Network->Id, // LOGULONG
        Interface->State // LOGIfState
        );

    CnIssueEvent(
        ClusnetEventNetInterfaceUp,
        Interface->Node->Id,
        Interface->Network->Id
        );

    //
    // Release the reference on the network object.
    //
    CnAcquireLock(&(Network->Lock), &(Network->Irql));

    CnpDereferenceNetwork(Network);

    return;

}  // CnpReconnectLocalInterface


VOID
CnpReconnectLocalInterfaceWrapper(
    IN PDEVICE_OBJECT DeviceObject,
    IN PVOID          Context
    )
{
    PCNP_WMI_RECONNECT_WORKER_CONTEXT context = Context;
    PCNP_NETWORK                      network = NULL;
    PCNP_INTERFACE                    interface = NULL;
    CN_IRQL                           nodeTableIrql;
    BOOLEAN                           acquired = FALSE;
    PLIST_ENTRY                       entry;

    CnVerifyCpuLockMask(
        0,                  // Required
        0xFFFFFFFF,         // Forbidden
        0                   // Maximum
        );

    acquired = CnAcquireResourceExclusive(
                   CnpWmiNdisMediaStatusResource,
                   TRUE
                   );
    
    CnAssert(acquired == TRUE);

    CnAcquireLock(&CnpNodeTableLock, &nodeTableIrql);

    if (CnpLocalNode != NULL) {
        CnAcquireLockAtDpc(&(CnpLocalNode->Lock));
        CnReleaseLockFromDpc(&CnpNodeTableLock);
        CnpLocalNode->Irql = nodeTableIrql;

        network = CnpFindNetwork(context->NetworkId);
    
        if (network != NULL) {

            //
            // Only go through the reconnect if the network
            // is currently marked as locally disconnected.
            // It is possible that we have already received
            // and processed a WMI connect event.
            //
            if (CnpIsNetworkLocalDisconn(network)) {

                for (entry = CnpLocalNode->InterfaceList.Flink;
                     entry != &(CnpLocalNode->InterfaceList);
                     entry = entry->Flink
                    )
                {
                    interface = CONTAINING_RECORD(
                                    entry,
                                    CNP_INTERFACE,
                                    NodeLinkage
                                    );

                    if (interface->Network == network) {

                        CnpReconnectLocalInterface(
                            interface,
                            network
                            );

                        //
                        // Both node and network locks
                        // were released.
                        //

                        break;

                    } else {
                        interface = NULL;
                    }
                }
            
            } else {

                CnTrace(
                    CXPNP, CnpReconnectLocalInterfaceWrapperRedundant,
                    "[CXPNP] Network ID %u is already connected; "
                    "aborting reconnect in wrapper.\n",
                    network->Id // LOGULONG
                    );
            }

            if (interface == NULL) {
                CnReleaseLock(&(network->Lock), network->Irql);
            }
        }

        if (interface == NULL) {
            CnReleaseLock(&(CnpLocalNode->Lock), CnpLocalNode->Irql);
        }
    } else {
        CnReleaseLock(&CnpNodeTableLock, nodeTableIrql);
    }
    
    //
    // Release resource
    //
    if (acquired) {
        CnReleaseResourceForThread(
            CnpWmiNdisMediaStatusResource,
            (ERESOURCE_THREAD) PsGetCurrentThread()
            );
    }
    
    //
    // Free the workitem and context
    //
    IoFreeWorkItem(context->WorkItem);
    CnFreePool(context);

    CnVerifyCpuLockMask(
        0,                  // Required
        0xFFFFFFFF,         // Forbidden
        0                   // Maximum
        );

    return;

}  // CnpReconnectLocalInterfaceWrapper


VOID
CnpDisconnectLocalInterface(
    PCNP_INTERFACE Interface,
    PCNP_NETWORK   Network
    )
/*++

Routine Description:

    Changes a local interface from being connected to
    disconnected. Called in response to a WMI NDIS media status
    disconnect event or an NDIS query that returns media 
    disconnected.
   
Arguments:

    Interface - local interface that is reconnected
    
    Network - network associated with Interface
    
Return value:

    None
    
Notes:

    Called with CnpWmiNdisMediaStatusResource, local node lock,
    and Network lock held.
    
    Returns with CnpWmiNdisMediaStatusResource held but neither
    lock held.
    
--*/    
{
    CnVerifyCpuLockMask(
        (CNP_NODE_OBJECT_LOCK | CNP_NETWORK_OBJECT_LOCK),   // Required
        0,                                                  // Forbidden
        CNP_NETWORK_OBJECT_LOCK_MAX                         // Maximum
        );

    CnTrace(
        CXPNP, CnpDisconnectLocalInterface,
        "[CXPNP] Interface for network ID %u "
        "disconnected.\n",
        Network->Id // LOGULONG
        );

    //
    // Set the local disconnect flag in the network
    // object
    //
    Network->Flags |= CNP_NET_FLAG_LOCALDISCONN;

    //
    // Reference the network so it can't go away while we
    // reprioritize the associated interfaces.
    //
    CnpReferenceNetwork(Network);

    //
    // Fail the interface. This call releases the
    // network lock.
    //
    CnpFailInterface(Interface);

    //
    // Release the node lock before walking the interfaces
    // on the network.
    //
    CnReleaseLock(&(CnpLocalNode->Lock), CnpLocalNode->Irql);

    //
    // Update the CurrentInterface for the other
    // nodes in the cluster to reflect the disconnected
    // status of the local interface.
    //
    CnpWalkInterfacesOnNetwork(
        Network, 
        CnpWmiPnpUpdateCurrentInterface
        );

    //
    // Issue InterfaceFailed event to the cluster
    // service.
    //
    CnTrace(
        CXPNP, CnpLocalDisconnectIssueEvent,
        "[CXPNP] Issuing InterfaceFailed event "
        "for node %u on net %u, previous I/F state = %!ifstate!.",
        Interface->Node->Id, // LOGULONG
        Interface->Network->Id, // LOGULONG
        Interface->State // LOGIfState
        );

    CnIssueEvent(
        ClusnetEventNetInterfaceFailed,
        Interface->Node->Id,
        Interface->Network->Id
        );

    //
    // Release the reference on the network object.
    //
    CnAcquireLock(&(Network->Lock), &(Network->Irql));

    CnpDereferenceNetwork(Network);

    return;

}  // CnpDisconnectLocalInterface


VOID
CnpWmiNdisMediaStatusConnectCallback(
    IN PVOID Wnode,
    IN PVOID Context
    )
{
    PWNODE_SINGLE_INSTANCE wnode = (PWNODE_SINGLE_INSTANCE) Wnode;
    PCNP_INTERFACE         interface;
    PCNP_NETWORK           network;
    PLIST_ENTRY            entry;
    CN_IRQL                nodeTableIrql;
    BOOLEAN                acquired = FALSE;

    CnVerifyCpuLockMask(
        0,                  // Required
        0xFFFFFFFF,         // Forbidden
        0                   // Maximum
        );

    IF_CNDBG(CN_DEBUG_CONFIG) {
        CNPRINT((
            "[CX] Received WMI NDIS status media connect event.\n"
            ));
    }

    //
    // Serialize events as much as possible, since clusnet spinlocks
    // may be acquired and released repeatedly. 
    //
    // Note that there may not be any guarantees that WMI event
    // ordering is guaranteed. The fallback mechanism for clusnet
    // is heartbeats -- if a heartbeat is received on an interface,
    // we know the interface is connected.
    //
    acquired = CnAcquireResourceExclusive(
                   CnpWmiNdisMediaStatusResource,
                   TRUE
                   );

    CnAssert(acquired == TRUE);

    //
    // Figure out if this callback is for one of this node's
    // registered interfaces by comparing the WMI provider ID
    // in the WNODE header to the WMI provider IDs of this
    // node's adapters.
    //
    CnAcquireLock(&CnpNodeTableLock, &nodeTableIrql);

    if (CnpLocalNode != NULL) {
        CnAcquireLockAtDpc(&(CnpLocalNode->Lock));
        CnReleaseLockFromDpc(&CnpNodeTableLock);
        CnpLocalNode->Irql = nodeTableIrql;

        network = NULL;

        for (entry = CnpLocalNode->InterfaceList.Flink;
             entry != &(CnpLocalNode->InterfaceList);
             entry = entry->Flink
            )
        {
            interface = CONTAINING_RECORD(
                            entry,
                            CNP_INTERFACE,
                            NodeLinkage
                            );

            if (wnode->WnodeHeader.ProviderId
                == interface->AdapterWMIProviderId) {
                
                //
                // Found the local interface corresponding to this
                // address.
                //
                network = interface->Network;

                //
                // Start by checking if we believe the network is
                // currently disconnected.
                //
                CnAcquireLockAtDpc(&(network->Lock));
                network->Irql = DISPATCH_LEVEL;

                if (CnpIsNetworkLocalDisconn(network)) {

                    CnTrace(
                        CXPNP, CxWmiNdisConnectNet,
                        "[CXPNP] Interface for network ID %u "
                        "connected.\n",
                        network->Id // LOGULONG
                        );

                    CnpReconnectLocalInterface(interface, network);
                    
                    //
                    // Node and network locks were released
                    //

                } else {

                    CnTrace(
                        CXPNP, CxWmiNdisConnectNetRedundant,
                        "[CXPNP] Ignoring redundant WMI NDIS connect "
                        "event for interface for network ID %u.\n",
                        network->Id // LOGULONG
                        );
                    
                    CnReleaseLockFromDpc(&(network->Lock));
                    CnReleaseLock(&(CnpLocalNode->Lock), CnpLocalNode->Irql);
                }
                
                break;
            }
        }

        if (network == NULL) {
            CnReleaseLock(&(CnpLocalNode->Lock), CnpLocalNode->Irql);
        }
    }
    else {
        CnReleaseLock(&CnpNodeTableLock, nodeTableIrql);
    }

    IF_CNDBG(CN_DEBUG_CONFIG) {
        if (network != NULL) {
            CNPRINT((
                "[CX] Interface for network ID %u connected.\n",
                network->Id
                ));
        } else {
            CNPRINT((
                "[CX] Unknown interface connected, provider id %lx\n",
                wnode->WnodeHeader.ProviderId
                ));
        }
    }

    //
    // Release resource
    //
    if (acquired) {
        CnReleaseResourceForThread(
            CnpWmiNdisMediaStatusResource,
            (ERESOURCE_THREAD) PsGetCurrentThread()
            );
    }
    
    CnVerifyCpuLockMask(
        0,                  // Required
        0xFFFFFFFF,         // Forbidden
        0                   // Maximum
        );

    return;

} // CnpWmiNdisMediaStatusConnectCallback


VOID
CnpWmiNdisMediaStatusDisconnectCallback(
    IN PVOID Wnode,
    IN PVOID Context
    )
{
    PWNODE_SINGLE_INSTANCE wnode = (PWNODE_SINGLE_INSTANCE) Wnode;
    PCNP_INTERFACE         interface;
    PCNP_NETWORK           network;
    PLIST_ENTRY            entry;
    CN_IRQL                nodeTableIrql;
    BOOLEAN                acquired = FALSE;

    CnVerifyCpuLockMask(
        0,                  // Required
        0xFFFFFFFF,         // Forbidden
        0                   // Maximum
        );

    IF_CNDBG(CN_DEBUG_CONFIG) {
        CNPRINT((
            "[CX] Received WMI NDIS status media disconnect event.\n"
            ));
    }

    CnTrace(CXPNP, CxWmiNdisDisconnect,
        "[CXPNP] Received WMI NDIS status media disconnect event.\n"
        );

    //
    // Serialize events as much as possible, since clusnet spinlocks
    // may be acquired and released repeatedly. 
    //
    // Note that there may not be any guarantees that WMI event
    // ordering is guaranteed. The fallback mechanism for clusnet
    // is heartbeats -- if a heartbeat is received on an interface,
    // we know the interface is connected.
    //
    acquired = CnAcquireResourceExclusive(
                   CnpWmiNdisMediaStatusResource,
                   TRUE
                   );

    CnAssert(acquired == TRUE);

    //
    // Figure out if this callback is for one of this node's
    // registered interfaces by comparing the WMI provider ID
    // in the WNODE header to the WMI provider IDs of this
    // node's adapters.
    //
    CnAcquireLock(&CnpNodeTableLock, &nodeTableIrql);

    if (CnpLocalNode != NULL) {
        CnAcquireLockAtDpc(&(CnpLocalNode->Lock));
        CnReleaseLockFromDpc(&CnpNodeTableLock);
        CnpLocalNode->Irql = nodeTableIrql;

        network = NULL;

        for (entry = CnpLocalNode->InterfaceList.Flink;
             entry != &(CnpLocalNode->InterfaceList);
             entry = entry->Flink
            )
        {
            interface = CONTAINING_RECORD(
                            entry,
                            CNP_INTERFACE,
                            NodeLinkage
                            );

            if (wnode->WnodeHeader.ProviderId
                == interface->AdapterWMIProviderId) {
                
                //
                // Found the local interface object corresponding
                // to this adapter.
                //
                network = interface->Network;

                CnAcquireLockAtDpc(&(network->Lock));
                network->Irql = DISPATCH_LEVEL;

                CnpDisconnectLocalInterface(interface, network);

                break;
            }
        }

        if (network == NULL) {
            CnReleaseLock(&(CnpLocalNode->Lock), CnpLocalNode->Irql);
        }
    }
    else {
        CnReleaseLock(&CnpNodeTableLock, nodeTableIrql);
    }

    IF_CNDBG(CN_DEBUG_CONFIG) {
        if (network != NULL) {
            CNPRINT((
                "[CX] Interface for network ID %u disconnected.\n",
                network->Id
                ));
        } else {
            CNPRINT((
                "[CX] Unknown interface disconnected, provider id %lx\n",
                wnode->WnodeHeader.ProviderId
                ));
        }
    }

    //
    // Release resource
    //
    if (acquired) {
        CnReleaseResourceForThread(
            CnpWmiNdisMediaStatusResource,
            (ERESOURCE_THREAD) PsGetCurrentThread()
            );
    }
    
    CnVerifyCpuLockMask(
        0,                  // Required
        0xFFFFFFFF,         // Forbidden
        0                   // Maximum
        );

    return;

} // CnpWmiNdisMediaStatusDisconnectCallback