/*++

Module Name:

    device.c - Device handling events for example driver.

Abstract:

   This file contains the device entry points and callbacks.
    
Environment:

    Kernel-mode Driver Framework

--*/

#include "precomp.h"
#include "driver.h"
#include "device.tmh"

EVT_WDF_OBJECT_CONTEXT_DESTROY EvtDestroyDeviceCallback;

#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, nattraverse_kmdffilterCreateDevice)
#pragma alloc_text (PAGE, EvtDestroyDeviceCallback)
#endif

NTSTATUS
nattraverse_kmdffilterCreateDevice(
    _Inout_ PWDFDEVICE_INIT DeviceInit
    )
/*++

Routine Description:

    Worker routine called to create a device and its software resources.

Arguments:

    DeviceInit - Pointer to an opaque init structure. Memory for this
                    structure will be freed by the framework when the WdfDeviceCreate
                    succeeds. So don't access the structure after that point.

Return Value:

    NTSTATUS

--*/
{
    WDF_OBJECT_ATTRIBUTES   deviceAttributes;
    PDEVICE_CONTEXT         deviceContext;
    NTSTATUS                status;
    WDFDEVICE               controlDevice;
    DECLARE_CONST_UNICODE_STRING(ntDeviceName, NATTRAVERSE_DEVICE_NAME) ;
    DECLARE_CONST_UNICODE_STRING(symbolicLinkName, NATTRAVERSE_SYMBOLIC_NAME) ;
    PAGED_CODE();
    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "%!FUNC! Entry");
    //
    // Set exclusive to TRUE so that no more than one app can talk to the
    // control controlDevice at any time.
    //
    WdfDeviceInitSetExclusive(DeviceInit, TRUE);
    WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoBuffered);
    status = WdfDeviceInitAssignName(DeviceInit, &ntDeviceName);
    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "WdfDeviceInitAssignName failed %!STATUS!", status);
        goto End;
    }
    WdfControlDeviceInitSetShutdownNotification(
        DeviceInit,
        nattraverse_kmdffilterShutdown,
        WdfDeviceShutdown
        );
    //
    // Specify the size of controlDevice context
    //
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT);
    deviceAttributes.EvtDestroyCallback = EvtDestroyDeviceCallback;
    status = WdfDeviceCreate(
        &DeviceInit,
        &deviceAttributes,
        &controlDevice);
    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "WdfDeviceCreate failed %!STATUS!", status);
        goto End;
    }
    //
    // Create a symbolic link for the control object so that usermode can open
    // the controlDevice.
    //
    status = WdfDeviceCreateSymbolicLink(controlDevice,
                                &symbolicLinkName);
    if (!NT_SUCCESS(status)) {
        //
        // Control controlDevice will be deleted automatically by the framework.
        //
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "WdfDeviceCreateSymbolicLink failed %!STATUS!", status);
        goto End;
    }
    //
    // Get the controlDevice context and initialize it. WdfObjectGet_DEVICE_CONTEXT is an
    // inline function generated by WDF_DECLARE_CONTEXT_TYPE macro in the
    // controlDevice.h header file. This function will do the type checking and return
    // the controlDevice context. If you pass a wrong object  handle
    // it will return NULL and assert if run under framework verifier mode.
    //
    deviceContext = WdfObjectGet_DEVICE_CONTEXT(controlDevice);
    deviceContext->PrivateDeviceData = 0;
    //
    // Initialize WFP environment
    //
    status = nattravfilterCoInitialize(
        WdfDeviceWdmGetDeviceObject(controlDevice)
        );
    if (!NT_SUCCESS(status))
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "nattravfilterCoInitialize failed %!STATUS!", status);
        goto End;
    }
    //
    // Initialize the I/O Package and any Queues
    //
    status = nattraverse_kmdffilterQueueInitialize(controlDevice);
    if (!NT_SUCCESS(status))
    {
        //
        // Control controlDevice will be deleted automatically by the framework.
        //
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "nattraverse_kmdffilterQueueInitialize failed %!STATUS!", status);
        goto End;
    }
    //
    // Control devices must notify WDF when they are done initializing.   I/O is
    // rejected until this call is made.
    //
    WdfControlFinishInitializing(controlDevice);
End:
    //
    // If the controlDevice is created successfully, framework would clear the
    // DeviceInit value. Otherwise controlDevice create must have failed so we
    // should free the memory ourself.
    //
    if (DeviceInit != NULL) {
        WdfDeviceInitFree(DeviceInit);
    }
    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "%!FUNC! Exit");
    return status;
}

VOID
nattraverse_kmdffilterShutdown(
    WDFDEVICE Device
    )
/*++

Routine Description:
    Callback invoked when the machine is shutting down.  If you register for
    a last chance shutdown notification you cannot do the following:
    o Call any pageable routines
    o Access pageable memory
    o Perform any file I/O operations

    If you register for a normal shutdown notification, all of these are
    available to you.

    This function implementation does nothing, but if you had any outstanding
    file handles open, this is where you would close them.

Arguments:
    Device - The device which registered the notification during init

Return Value:
    None

  --*/

{
    UNREFERENCED_PARAMETER(Device);
    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "%!FUNC! Entry");
    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "%!FUNC! Exit");
    return;
}

VOID EvtDestroyDeviceCallback(
    __in  WDFOBJECT Object
    )
{
    KLOCK_QUEUE_HANDLE  stFilterDataLock;
    UNREFERENCED_PARAMETER(Object);
    PAGED_CODE();
    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "%!FUNC! Entry");
    KeAcquireInStackQueuedSpinLock(
        &g_nFilterDataLock,
        &stFilterDataLock
        );
    if(NULL != g_pFilterInitStruct)
    {
        FilterDataCleanup();
    }
    KeReleaseInStackQueuedSpinLock(&stFilterDataLock);
    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DEVICE, "%!FUNC! Exit");
    return;
}
