/*
 * ----------------------------------------------------------------------------
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer,
 *    without modification.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY.
 *
 * ----------------------------------------------------------------------------
 *
 * Alternatively, this software may be distributed under the terms of the
 * terms of the GNU General Public License version 2 as published by the
 * Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 * ----------------------------------------------------------------------------
 *
 * For history of changes, ChangeLog.txt
 *
 * This header file describes the register of the Quantis PCI card. The code has been adapted from
 * a microsoft provided sample file.
 *
 Module Name:

    Quantis.c - manage the quantis card

Environment:

    Kernel mode
 */

#include "CommonWindows.h"

QUANTIS_EXTERN_C_START

#include "Quantis.h"
#include "QuantisOp.h"
#include "Quantis.tmh" // auto-generated by WPP

//
// Make sure the initialization code is removed from memory after use.
//

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, QuantisEvtDeviceAdd)
#pragma alloc_text(PAGE, QuantisContextCleanup)
#pragma alloc_text(PAGE, QuantisEvtDeviceD0Exit)
#endif


NTSTATUS
QuantisEvtDeviceAdd(
    __in    WDFDRIVER        Driver,
    __inout PWDFDEVICE_INIT  DeviceInit
    )
/*++

Routine Description:

    EvtDeviceAdd is called by the framework in response to AddDevice
    call from the PnP manager.  It is responsible for initializing and
    creating a WDFDEVICE object.

    Any work that should be done after the object is created should be
    deferred until EvtDeviceSoftwareInit, as that callback will be made
    with the device lock held (if there is one.)
    
Arguments:

    Driver - Handle to a framework driver object created in DriverEntry

    DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure.

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS                   status = STATUS_SUCCESS;
    WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
    WDF_OBJECT_ATTRIBUTES       fdoAttributes;
    WDF_INTERRUPT_CONFIG        interruptConfig;
    WDF_OBJECT_ATTRIBUTES       interruptAttributes;
    WDF_IO_QUEUE_CONFIG         ioQueueConfig;
    PQUANTIS_DEVICE_EXTENSION    devExt;
    WDFQUEUE                    hQueue;
    WDFDEVICE                   device;
    PQUANTIS_DRIVER_EXTENSION   drvExt;
    
    PAGED_CODE();

	KdPrint(("QuantisEvtDeviceAdd: 0x%p\n", Driver));
    DoTraceMessage(QUANTIS_DEBUG,"QuantisEvtDeviceAdd: 0x%p", Driver);

    //
    // Zero out the PnpPowerCallbacks structure.
    //
    WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);

    //
    // Set Callbacks for any of the functions we are interested in.
    // If no callback is set, Framework will take the default action
    // by itself.
    //
    pnpPowerCallbacks.EvtDevicePrepareHardware = QuantisEvtDevicePrepareHardware;
    pnpPowerCallbacks.EvtDeviceReleaseHardware = QuantisEvtDeviceReleaseHardware;
    pnpPowerCallbacks.EvtDeviceD0Entry         = QuantisEvtDeviceD0Entry;
    pnpPowerCallbacks.EvtDeviceD0Exit          = QuantisEvtDeviceD0Exit;

    //
    // Register the PnP Callbacks..
    //
    WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);

	KdPrint(("QuantisEvtDeviceAdd: Pnp CallBack set.\n"));
    DoTraceMessage(QUANTIS_DEBUG,"QuantisEvtDeviceAdd: Pnp CallBack set.");
    
    //
    // Set various attributes for this device
    //
    WdfDeviceInitSetIoType( DeviceInit, WdfDeviceIoDirect );

    //Add to the device object, the specific extensions for the Quantis card.
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&fdoAttributes, QUANTIS_DEVICE_EXTENSION);
    fdoAttributes.EvtCleanupCallback = QuantisContextCleanup;
    //
    // We want all the queue callbacks, cancel routine, and DpcForIsr to be serialized
    // at the device level, so we don't have worry about any synchronization issues.
    //
    fdoAttributes.SynchronizationScope = WdfSynchronizationScopeDevice;

    DoTraceMessage(QUANTIS_DEBUG,"QuantisEvtDeviceAdd: Before device creation.");
    
    status = WdfDeviceCreate( &DeviceInit, &fdoAttributes, &device );

    if ( !NT_SUCCESS(status) ) {
	    KdPrint(("WdfDeviceInitialize failed %!STATUS!\n", status));
        DoTraceMessage(QUANTIS_ERROR,"WdfDeviceInitialize failed %!STATUS!", status);
        return status;
    }

    //
    // Device Initialization is complete.
    // Get the Device Extension and initialize it.
    //
    devExt = QuantisGetDevExt(device);

    devExt->Device = device;

	KdPrint(("PDO 0x%p, FDO 0x%p, DevExt 0x%p\n",
                   WdfDeviceWdmGetPhysicalDevice(device),
                   WdfDeviceWdmGetDeviceObject( device ), devExt));
    DoTraceMessage(QUANTIS_INFO, "PDO 0x%p, FDO 0x%p, DevExt 0x%p",
                   WdfDeviceWdmGetPhysicalDevice(device),
                   WdfDeviceWdmGetDeviceObject( device ), devExt);

    //
    // This device generates an interrupt.  So create an interrupt object which
    // will later be associated with the devices resources and connected
    // by the Framework.
    //

    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&interruptAttributes, INTERRUPT_DATA);

    //
    // Configure the Interrupt object
    //
    WDF_INTERRUPT_CONFIG_INIT(&interruptConfig,
                              QuantisEvtInterruptIsr,
                              QuantisEvtInterruptDpc);

    status = WdfInterruptCreate(device,
                                &interruptConfig,
                                &interruptAttributes,
                                &devExt->WdfInterrupt);
    if (!NT_SUCCESS (status))
    {
	    KdPrint(("WdfInterruptCreate failed: %!STATUS!\n", status));
        DoTraceMessage(QUANTIS_ERROR,"WdfInterruptCreate failed: %!STATUS!\n", status);
        return status;
    }

    //
    // Register I/O callbacks.
    //
    // Create a sequential IO Queue for serial operation. That means all the requests (Read/Write
    // & IOCTL) are serialized to the device. Until the driver completes the request presented to it,
    // the framework will not schedule another one. The requests held in the framework will be
    // cancelled automatically if the source of request (application) terminate or cancels it.
    //
    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE( &ioQueueConfig,
                              WdfIoQueueDispatchSequential);

    ioQueueConfig.EvtIoRead = QuantisEvtIoRead;
    // The Quantis number generator is a read-only device
    // ioQueueConfig.EvtIoWrite = QuantisEvtIoWrite;
    ioQueueConfig.EvtIoDeviceControl = QuantisEvtIoDeviceControl;
    
    status = WdfIoQueueCreate( device,
                               &ioQueueConfig,
                               WDF_NO_OBJECT_ATTRIBUTES,
                               &hQueue );
    if (!NT_SUCCESS (status)) {
        //
        // We don't have worry about deleting the device here because framework will automatically
        // cleanup that when the driver unloads.
        //
        DoTraceMessage(QUANTIS_ERROR,"WdfIoQueueCreate failed %!STATUS!", status);
        return status;
    }
    
	KdPrint(("QuantisEvtDeviceAdd: Before interface creation.\n"));
    DoTraceMessage(QUANTIS_DEBUG,"QuantisEvtDeviceAdd: Before interface creation.");
    
    //
    // Register an interface so that application can find and talk to us.
    // NOTE: See the note in Public.h concerning this GUID value.
    //
    status = WdfDeviceCreateDeviceInterface( device,
                                             (LPGUID) &GUID_DEVINTERFACE_QUANTIS_PCI,
                                             NULL );

    if (!NT_SUCCESS(status)) {
	    KdPrint(("<-- QuantisAddDevice: WdfDeviceCreateDeviceInterface failed %!STATUS!\n", status));
        DoTraceMessage(QUANTIS_ERROR,
                "<-- QuantisAddDevice: WdfDeviceCreateDeviceInterface failed %!STATUS!", status);
        return status;
    }

    devExt->MaximumTransferLength = QUANTIS_DEVICE_BUFFER_SIZE;

    //
    // Set the maximum physical pages for now, but this value may change if
    // there aren't enough map registers
    //
    devExt->MaximumPhysicalPages = MAXIMUM_PHYSICAL_PAGES;

	KdPrint(("QuantisEvtDeviceAdd: Exiting normally\n"));
    DoTraceMessage(QUANTIS_DEBUG,"QuantisEvtDeviceAdd: Exiting normally");
    
    return status;
}

NTSTATUS
QuantisEvtDevicePrepareHardware(
    __in WDFDEVICE       Device,
    __in WDFCMRESLIST   Resources,
    __in WDFCMRESLIST   ResourcesTranslated
    )
/*++

Routine Description:

    EvtDevicePrepareHardware event callback performs operations that are necessary
    to use the device's control registers.

Arguments:

    Device - Handle to a framework device object.

    Resources - Handle to a collection of framework resource objects.
                This collection identifies the raw (bus-relative) hardware
                resources that have been assigned to the device.

    ResourcesTranslated - Handle to a collection of framework resource objects.
                This collection identifies the translated (system-physical)
                hardware resources that have been assigned to the device.
                The resources appear from the CPU's point of view.
                Use this list of resources to map I/O space and
                device-accessible memory into virtual address space
                
   Attention: Due to use of Spin Lock, this function is running at IRQL 2.
              Spin lock are mandatory since the reading of the variable is
              done at IRQL 2.


Return Value:

    WDF status code.

    Let us not worry about cleaning up the resources here if we fail start,
    because the PNP manager will send a remove-request and we will free all
    the allocated resources in QuantisEvtDeviceReleaseHardware.

	The Quantis PCI card seems to have three Base Address registers.
	Only one is used, generally the second one. Unfortunately, the card
	physical documentation has no precise description of these three registers.
--*/
{
    ULONG                               i,NbResources;
    NTSTATUS                            status          = STATUS_SUCCESS;
    PQUANTIS_DEVICE_EXTENSION           devExt          = NULL;
    PQUANTIS_DRIVER_EXTENSION           drvExt          = NULL;
    BOOLEAN                             foundPort       = FALSE;
    PHYSICAL_ADDRESS                    portBasePA      = {0};
    ULONG                               portCount       = 0;
	int                                 numberOfBARs    = 0; //Base Address register found on the card.
    PCM_PARTIAL_RESOURCE_DESCRIPTOR     desc;
    WDFDRIVER                           driver;

    //
    // The Resources collection is not used for PCI devices, since the PCI
    // bus driver manages the device's PCI Base Address Registers.
    //

    UNREFERENCED_PARAMETER( Resources );

	KdPrint(("QuantisEvtDevicePrepareHardware: Entering function.\n"));
    DoTraceMessage(QUANTIS_DEBUG,"QuantisEvtDevicePrepareHardware: Entering function.");

    devExt = QuantisGetDevExt(Device);

    //
    // Parse the resource list and save the resource information.
    //
    NbResources=WdfCmResourceListGetCount(ResourcesTranslated);
    for (i=0; i < NbResources; i++) {

	    KdPrint(("The %d (from %d) translated ressource is used\n", i,
                       NbResources));
        DoTraceMessage(QUANTIS_DEBUG,"The %d (from %d) translated ressource is used", i,
                       NbResources);
        
        desc = WdfCmResourceListGetDescriptor(ResourcesTranslated, i);

        if(!desc) {
		    KdPrint(("WdfResourceCmGetDescriptor failed\n"));
            DoTraceMessage(QUANTIS_ERROR,"WdfResourceCmGetDescriptor failed");
            return STATUS_DEVICE_CONFIGURATION_ERROR;
        }

		KdPrint(("The type of resource is %d.\n", desc->Type));
        DoTraceMessage(QUANTIS_DEBUG,"The type of resource is %d.", desc->Type);
                     
        switch (desc->Type) {

        case CmResourceTypePort:
            
            portBasePA = desc->u.Port.Start;
            portCount  = desc->u.Port.Length;

            devExt->PortMapped =
                (desc->Flags & CM_RESOURCE_PORT_IO) ? FALSE : TRUE;

            foundPort = TRUE;

			KdPrint(("The physical address of PortBased is 0x%u-0x%lu, the number of port is %u.\n The IO space is PortMapped %c\n",
                           portBasePA.u.HighPart,portBasePA.u.LowPart,portCount,devExt->PortMapped));
            DoTraceMessage(QUANTIS_DEBUG,
                           "The physical address of PortBased is 0x%u-0x%lu, the number of port is %u. The IO space is PortMapped %c",
                           portBasePA.u.HighPart,portBasePA.u.LowPart,portCount,devExt->PortMapped);
            //
            // Map in the single IO Space resource.
            //
            if (devExt->PortMapped) {

                devExt->PortBase =
                    (PUCHAR) MmMapIoSpace( portBasePA, portCount, MmNonCached );

                if (!devExt->PortBase) {

				    KdPrint(("Unable to map port range 0x%p, length %d\n",
                                   devExt->PortBase, devExt->PortCount));
                    DoTraceMessage(QUANTIS_ERROR,"Unable to map port range 0x%p, length %d",
                                   devExt->PortBase, devExt->PortCount);

                    return STATUS_INSUFFICIENT_RESOURCES;
                }

                devExt->PortCount = portCount;

            } else {

                devExt->PortBase  = (PUCHAR)(ULONG_PTR) portBasePA.QuadPart;
                devExt->PortCount = portCount;
            }

            devExt->Regs = (PQUANTIS_REG) devExt->PortBase;

            break;

		case CmResourceTypeMemory:

            numberOfBARs++;
			
            KdPrint(("Memory mapped register %d:(%x:%x) Length:(%d)\n",
			         numberOfBARs,
                     desc->u.Memory.Start.LowPart,desc->u.Memory.Start.HighPart,
                     desc->u.Memory.Length));
            //
            // Our CSR memory space should be 0x1000 in size.
            //
            //ASSERT(descriptor->u.Memory.Length == 0x1000);

			if (numberOfBARs == 2)
			{ /* Only the second register seems to have relevant data */
			    portBasePA=desc->u.Memory.Start;
				devExt->PortCount=sizeof(QUANTIS_REG); //desc->u.Memory.Length is 256kb which is too much
			    devExt->PortBase = (PUCHAR) MmMapIoSpace( portBasePA, devExt->PortCount, MmNonCached );

                if (!devExt->PortBase) {
                    KdPrint(("Unable to map memory range (%x:%x), length %d\n",
					         portBasePA.HighPart, 
							 portBasePA.LowPart,
							 devExt->PortCount));
                    DoTraceMessage(QUANTIS_ERROR,"Unable to map memory range (%x:%x), length %d\n",
					         portBasePA.HighPart, 
							 portBasePA.LowPart,
							 devExt->PortCount);

                    return STATUS_INSUFFICIENT_RESOURCES;
                }
                KdPrint(("Address used for Quantis card 0x%p, length %d\n",devExt->PortBase, devExt->PortCount));
                DoTraceMessage(QUANTIS_INFO,"Address used for Quantis card 0x%p, length %d",
                                devExt->PortBase, devExt->PortCount);
				devExt->Regs = (PQUANTIS_REG) devExt->PortBase;
				devExt->PortMapped = FALSE;
				/* Does not harm even if module is not present */
				devExt->ModulesStatusMask=MODULE1+MODULE2+MODULE3+MODULE4;
				foundPort = TRUE;
			}
            break;

        default:
		    KdPrint(("Unknown resource type %d\n", desc->Type));
            DoTraceMessage(QUANTIS_WARNING, "Unknown resource type %d", desc->Type);
            break;
        }

    }

    if (!foundPort) {
	    KdPrint(("Missing hardware resources\n"));
        DoTraceMessage(QUANTIS_ERROR,"Missing hardware resources");
        return STATUS_DEVICE_CONFIGURATION_ERROR;
    }
 
    /* Everything OK, the number of controled card is increased. */
    if ((driver=WdfGetDriver()) != NULL){
       drvExt=QuantisGetDrvExt(driver);
       /* The writing of the number of cards should be protected by a spin
          lock since the reading of the variable is executed at IRQL 2.*/
       WdfSpinLockAcquire(drvExt->NbCardsLock);
       drvExt->NumberOfCards++;
       WdfSpinLockRelease(drvExt->NbCardsLock);
    }
    else{
        /* Should normally never arise */
        KdPrint(("QuantisEvtDevicePrepareHardware: Cannot get the driver handle\n"));
        DoTraceMessage(QUANTIS_ERROR,"QuantisEvtDevicePrepareHardware: Cannot get the Wdf Driver handle.\n");
        return STATUS_WDF_INTERNAL_ERROR;
    }

    return STATUS_SUCCESS;
}

NTSTATUS
QuantisEvtDeviceReleaseHardware (
    __in WDFDEVICE       hDevice,
    __in WDFCMRESLIST   ResourcesTranslated
    )
/*++

Routine Description:

    EvtDeviceReleaseHardware is called by the framework whenever the PnP manager
    is revoking ownership of our resources.  This may be in response to either
    IRP_MN_STOP_DEVICE or IRP_MN_REMOVE_DEVICE.  The callback is made before
    passing down the IRP to the lower driver.

    In this callback, do anything necessary to free those resources.

Arguments:

    Device - Handle to a framework device object.

    ResourcesTranslated - Handle to a collection of framework resource objects.
                This collection identifies the translated (system-physical)
                hardware resources that have been assigned to the device.
                The resources appear from the CPU's point of view.
                Use this list of resources to map I/O space and
                device-accessible memory into virtual address space

   Attention: Due to use of Spin Lock, this function is running at IRQL 2.
              Spin lock are mandatory since the reading of the variable is
              done at IRQL 2.


Return Value:

    VOID
--*/
{
    PQUANTIS_DEVICE_EXTENSION   devExt = NULL;
    PQUANTIS_DRIVER_EXTENSION   drvExt = NULL;
    WDFDRIVER                   driver;
    
    UNREFERENCED_PARAMETER(ResourcesTranslated);

    devExt = QuantisGetDevExt(hDevice);

    //
    // Unmap the IO resource
    //
    if (devExt->PortBase) {

        if (devExt->PortMapped) {

            MmUnmapIoSpace( devExt->PortBase,  devExt->PortCount );

        }
        devExt->PortBase = NULL;
    }
  
    /* The card is no more accessible --> decrease the number of cards of the system */
    if ((driver=WdfGetDriver()) != NULL){
       drvExt=QuantisGetDrvExt(driver);
       /* The writing of the number of cards should be protected by a spin
          lock since the reading of the variable is executed at IRQL 2.*/
       WdfSpinLockAcquire(drvExt->NbCardsLock);
       if (drvExt->NumberOfCards > 0){
          drvExt->NumberOfCards--;
       }
       WdfSpinLockRelease(drvExt->NbCardsLock);
    }
    else{
        /* Should normally never arise */
        KdPrint(("QuantisEvtDeviceReleaseHardware: Cannot get the driver handle\n"));
        DoTraceMessage(QUANTIS_ERROR,"QuantisEvtDeviceReleaseHardware: Cannot get the Wdf Driver handle.\n");
        return STATUS_WDF_INTERNAL_ERROR;
    }
    
    return STATUS_SUCCESS;
}


VOID
QuantisContextCleanup(
    __in WDFOBJECT   Device
    )
/*++

Routine Description:

   EvtDeviceContextCleanup event callback must perform any operations that are
   necessary before the specified device is removed. The framework calls
   the driver's EvtDeviceRemove callback when the PnP manager sends
   an IRP_MN_REMOVE_DEVICE request to the driver stack. Function driver
   typically undo whatever they did in EvtDeviceSoftwareInit callback.

Note:

    It's not necessary to delete the WDFINTERRUPT object created in
    EvtDeviceSoftwareInit, since the Framework will automatically clean that
    up.

Arguments:

    Device - Handle to a framework device object.

Return Value:

    VOID

--*/
{
    PAGED_CODE();

    UNREFERENCED_PARAMETER(Device);
}


NTSTATUS
QuantisEvtDeviceD0Entry(
    __in IN  WDFDEVICE Device,
    __in IN  WDF_POWER_DEVICE_STATE PreviousState
    )
/*++

Routine Description:

    EvtDeviceD0Entry event callback must perform any operations that are
    necessary before the specified device is used.  It will be called every
    time the hardware needs to be (re-)initialized.  This includes after
    IRP_MN_START_DEVICE, IRP_MN_CANCEL_STOP_DEVICE, IRP_MN_CANCEL_REMOVE_DEVICE,
    IRP_MN_SET_POWER-D0.

    This function is not marked pageable because this function is in the
    device power up path. When a function is marked pagable and the code
    section is paged out, it will generate a page fault which could impact
    the fast resume behavior because the client driver will have to wait
    until the system drivers can service this page fault.

Arguments:

    Device - Handle to a framework device object.

    PreviousState - Device power state which the device was in most recently.
        If the device is being newly started, this will be
        PowerDeviceUnspecified.

Return Value:

    NTSTATUS

--*/
{
    PQUANTIS_DEVICE_EXTENSION           devExt          = NULL;

    UNREFERENCED_PARAMETER(PreviousState);

	KdPrint(("QuantisEvtDeviceD0Entry: Entering function.\n"));
    DoTraceMessage(QUANTIS_DEBUG,"QuantisEvtDeviceD0Entry: Entering function.");

    devExt = QuantisGetDevExt(Device);
    
	KdPrint(("Initializing module according to mask %x\n",devExt->ModulesStatusMask));
	DoTraceMessage(QUANTIS_DEBUG,"Initializing module according to mask %x\n",devExt->ModulesStatusMask);
	
	quantis_rng_enable_modules(devExt,devExt->ModulesStatusMask);

    return STATUS_SUCCESS;
}


NTSTATUS
QuantisEvtDeviceD0Exit(
    __in IN  WDFDEVICE Device,
    __in IN  WDF_POWER_DEVICE_STATE TargetState
    )
/*++

Routine Description:

   EvtDeviceD0Exit event callback must perform any operations that are
   necessary before the specified device is moved out of the D0 state.  If the
   driver needs to save hardware state before the device is powered down, then
   that should be done here.

Arguments:

    Device - Handle to a framework device object.

    TargetState - Device power state which the device will be put in once this
        callback is complete.

Return Value:

    NTSTATUS

--*/
{
    PQUANTIS_DEVICE_EXTENSION           devExt          = NULL;
	
    UNREFERENCED_PARAMETER(TargetState);

    PAGED_CODE();

	KdPrint(("QuantisEvtDeviceD0Exit: Entering function.\n"));
    DoTraceMessage(QUANTIS_DEBUG,"QuantisEvtDeviceD0Exit: Entering function.");

    devExt = QuantisGetDevExt(Device);
    
	KdPrint(("Initializing module according to mask %x\n",devExt->ModulesStatusMask));
	DoTraceMessage(QUANTIS_DEBUG,"Initializing module according to mask %x\n",devExt->ModulesStatusMask);
	
	quantis_rng_disable_modules(devExt,devExt->ModulesStatusMask);

    return STATUS_SUCCESS;
}

QUANTIS_EXTERN_C_END
