/*****************************************************************************
 * @ingroup WDT
 *
 * @file wdt.c
 *
 * @description
 *   This module contains the watchdog timer driver code.
 *
 * <DEFAULT_LICENSE_BEGIN>
 * Copyright (c) 2004 - 2005 Intel Corporation.
 * All rights reserved.
 *
 * The license is provided to Recipient and Recipient's Licensees under the
 * following terms.
 *
 * Redistribution and use in source and binary forms of the Software, with or
 * without modification, are permitted provided that the following conditions
 * are met:
 *
 * Redistributions of source code of the Software may retain the above
 * copyright notice, this list of conditions and the following disclaimer
 * Redistributions in binary form of the Software may reproduce the above
 * copyright notice, this list of conditions and the following disclaimer in
 * the documentation and/or materials provided with the distribution.
 *
 * Neither the name of Intel Corporation nor the names of its contributors
 * shall be used to endorse or promote products derived from this Software
 * without specific prior written permission.
 *
 * Intel hereby grants Recipient and Licensees a non-exclusive, worldwide,
 * royalty-free patent license under Licensed Patents to make, use, sell, offer
 * to sell, import and otherwise transfer the Software, if any, in source code
 * and object code form. This license shall include changes to the Software
 * that are error corrections or other minor changes to the Software that do
 * not add functionality or features when the Software is incorporated in any
 * version of an operating system that has been distributed under the GNU
 * General Public License 2.0 or later. This patent license shall apply to the
 * combination of the Software and any operating system licensed under the GNU
 * General Public License 2.0 or later if, at the time Intel provides the
 * Software to Recipient, such addition of the Software to the then publicly
 * available versions of such operating systems available under the GNU General
 * Public License 2.0 or later (whether in gold, beta or alpha form) causes
 * such combination to be covered by the Licensed Patents. The patent license
 * shall not apply to any other combinations which include the Software. NO
 * hardware per se is licensed hereunder.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MECHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR IT CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * ANY LOSS OF USE; DATA, OR PROFITS; OR BUSINESS INTERUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * <DEFAULT_LICENSE _END>
 ****************************************************************************/
/*
* INTEL CONFIDENTIAL
* Copyright 2007,2008,2009 Intel Corporation All Rights Reserved.
* 
* The source code contained or described herein and all documents related to the
* source code ("Material") are owned by Intel Corporation or its suppliers or
* licensors. Title to the Material remains with Intel Corporation or its
* suppliers and licensors. The Material may contain trade secrets and proprietary
* and confidential information of Intel Corporation and its suppliers and
* licensors, and is protected by worldwide copyright and trade secret laws and
* treaty provisions. No part of the Material may be used, copied, reproduced,
* modified, published, uploaded, posted, transmitted, distributed, or disclosed
* in any way without Intels prior express written permission.
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by Intel in writing.
* 
* Include any supplier copyright notices as supplier requires Intel to use.
* Include supplier trademarks or logos as supplier requires Intel to use,
* preceded by an asterisk.
* An asterisked footnote can be added as follows: 
*   *Third Party trademarks are the property of their respective owners.
* 
* Unless otherwise agreed by Intel in writing, you may not remove or alter this
* notice or any other notice embedded in Materials by Intel or Intels suppliers
* or licensors in any way.
* 
*  version: Embedded.X.1.0.3-127
*/

#include <NTDDK.h>
#include <wdf.h>
#include "wdt.h"
#include "driver.h"


/*----------------------------------------------------------------------------
 * Glocal Variables
 *----------------------------------------------------------------------------
 */
PDRIVER_OBJECT DriverObj;
USHORT  PortBase;
UCHAR   IRQ;


/*----------------------------------------------------------------------------
 * Formal Declaration
 *----------------------------------------------------------------------------
 */

NTSTATUS
WdtEvtDeviceAdd(IN WDFDRIVER Driver, IN PWDFDEVICE_INIT DeviceInit);

NTSTATUS
  WdtEvtDevicePrepareHardware(
    IN WDFDEVICE  Device,
    IN WDFCMRESLIST  ResourcesRaw,
    IN WDFCMRESLIST  ResourcesTranslated
    );

NTSTATUS
  WdtEvtDeviceReleaseHardware(
    IN WDFDEVICE  Device,
    IN WDFCMRESLIST  ResourcesTranslated
    );

VOID
  WdtIoDeviceControl(
	IN WDFQUEUE Queue,
	IN WDFREQUEST Request,
	IN size_t OutputBufferLength,
	IN size_t InputBufferLength,
	IN ULONG IoControlCode
    );

NTSTATUS
  WdtEvtDeviceD0Entry(
    IN WDFDEVICE  Device,
    IN WDF_POWER_DEVICE_STATE  PreviousState
    );

NTSTATUS
WdtEvtDeviceD0Exit(
    IN  WDFDEVICE Device,
    IN  WDF_POWER_DEVICE_STATE TargetState
    );



/*----------------------------------------------------------------------------
 *  Internal helper function declaration
 *----------------------------------------------------------------------------
 */

VOID WdtDpcInterruptRoutine(IN WDFDPC Dpc);

NTSTATUS get_wdt_config_info_from_lpc( USHORT *BaseAddr, UCHAR *IntNum );
NTSTATUS clr_intr_status( PDEVICE_EXTENSION DevExt );
BOOLEAN get_locked( PDEVICE_EXTENSION DevExt );
NTSTATUS initialize_interrupt( PDEVICE_EXTENSION pde );
BOOLEAN  isr( PKINTERRUPT Interrupt, PVOID Context );


/*!
 * DriverEntry
 *
 * This function will
 *    - Create a DEVICE_OBJECT corresponding to this driver
 *    - Initialize the DRIVER_OBJECT corresponding to this driver
 *    - Make the hardware visible to the Win32 subsystem by creating a symbolic
 *    link to the device name
 *    - Find and allocate the timer hardware
 *
 * @param DriverObject [IN] WDT driver object
 * @param RegistryPath [IN] registry path for the WDT driver
 *
 * @return always returns STATUS_SUCCESS
 *
 */
NTSTATUS DriverEntry(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
    )
{
	NTSTATUS status = STATUS_SUCCESS;
	WDF_DRIVER_CONFIG config;
	PCM_RESOURCE_LIST resourceList = NULL;
	BOOLEAN conflict;
	WDF_OBJECT_ATTRIBUTES objAttributes;

    KdPrint(("****** WDT: Entered Driver Entry ****** Registry at %s\n", RegistryPath));

	/* Store Driver Object */
	DriverObj = DriverObject;

    /* Initialize the Driver Config structure.. */
	WDF_DRIVER_CONFIG_INIT(&config, WdtEvtDeviceAdd);

    /* Init the driver synchronization scope */
	WDF_OBJECT_ATTRIBUTES_INIT(&objAttributes);
	objAttributes.SynchronizationScope = WdfSynchronizationScopeDevice;

    /* Create a WDFDRIVER object.   */
	status = WdfDriverCreate(
					DriverObject,
					RegistryPath,
					&objAttributes,
					&config,
					WDF_NO_HANDLE);
	if (!NT_SUCCESS(status))
	{
		KdPrint(("%s-DriverEntry: WdfDriverCreate failed.\n", DRIVERNAME));
		return status;
	}


    KdPrint(("%s-DriverEntry: Heading into Get info from lpc\n",
             DRIVERNAME));
	status = get_wdt_config_info_from_lpc(&PortBase, &IRQ);
	if (!NT_SUCCESS(status))
	{
		KdPrint(("%s-DriverEntry: get_wdt_config_info_from_lpc failed.\n",
		          DRIVERNAME));
		return status;
	}


	/* Set up Resource List for IoReportResourceForDetection */
	resourceList = ExAllocatePoolWithTag(PagedPool, sizeof(CM_RESOURCE_LIST)
                                 + sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR), TAG_WDT);

	RtlZeroMemory(resourceList, sizeof(CM_RESOURCE_LIST)
                 + sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR));

	resourceList->Count = 1;
	resourceList->List[0].PartialResourceList.Count = 1;
	resourceList->List[0].PartialResourceList.PartialDescriptors[0].Type = CmResourceTypeInterrupt;
	resourceList->List[0].PartialResourceList.PartialDescriptors[0].ShareDisposition = CmResourceShareDeviceExclusive;
	resourceList->List[0].PartialResourceList.PartialDescriptors[0].Flags = CM_RESOURCE_INTERRUPT_LATCHED;
	resourceList->List[0].PartialResourceList.PartialDescriptors[0].u.Interrupt.Level = IRQ;
	resourceList->List[0].PartialResourceList.PartialDescriptors[0].u.Interrupt.Vector = IRQ;


	/* IoReportResourceForDetection allows the OS to configure the interrupt
	   correctly */
	status = IoReportResourceForDetection(
									DriverObject,
									resourceList,
									sizeof(resourceList),
									NULL,
									NULL,
									0,
									&conflict );
	if (!NT_SUCCESS(status))
	{
		KdPrint(("%s-DriverEntry: IoReportResourceForDetection failed.\n",
		          DRIVERNAME));
		return status;
	}

	ExFreePoolWithTag(resourceList, TAG_WDT);

	KdPrint(("%s-DriverEntry: Done.\n", DRIVERNAME));

    return status;
}


/*
* Routine Description:
*
*    Initialize and create a new WDF object
*
* Arguments:
*
*    Driver - Handle to a driver object created in DriverEntry
*
*    DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure.
*
* Return Value:
*
*    NTSTATUS
*/
NTSTATUS
WdtEvtDeviceAdd(IN WDFDRIVER Driver, IN PWDFDEVICE_INIT DeviceInit)
{
	NTSTATUS status = STATUS_SUCCESS;
	WDFDEVICE WdfDevice;
	PDEVICE_EXTENSION pwdtData;
	WDF_IO_QUEUE_CONFIG queueConfig;
	WDF_OBJECT_ATTRIBUTES objAttributes;
	WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
	WDF_DPC_CONFIG dpcConfig;
    WDF_OBJECT_ATTRIBUTES dpcAttributes;

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

    /* These two callbacks set up and tear down hardware state,
       specifically that which only has to be done once. */
	pnpPowerCallbacks.EvtDevicePrepareHardware = WdtEvtDevicePrepareHardware;
	pnpPowerCallbacks.EvtDeviceReleaseHardware = WdtEvtDeviceReleaseHardware;

    /* These two callbacks perform operations needed when entering and
       exiting the D0 (working) power state. */
    pnpPowerCallbacks.EvtDeviceD0Entry = WdtEvtDeviceD0Entry;
	pnpPowerCallbacks.EvtDeviceD0Exit = WdtEvtDeviceD0Exit;

    /* Register the PnP and power callbacks. */
	WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);

	/* Set I/O type is Buffered */
	WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoBuffered);

    /* Init the device context */
	WDF_OBJECT_ATTRIBUTES_INIT(&objAttributes);

    /* Save context information in driver object structure  */
	WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(
		&objAttributes,
		DEVICE_EXTENSION
		);

    /* Create a framework device object. */
	status = WdfDeviceCreate(
		&DeviceInit,
		&objAttributes,
		&WdfDevice
		);
	if ( !NT_SUCCESS(status) )
	{
		KdPrint(("%s-WDTEvtDeviceAdd: WdfDeviceCreate failed.\n", DRIVERNAME));
        return status;
	}


    //
    // Get the device context by using the accessor function specified in
    // the WDF_DECLARE_CONTEXT_TYPE_WITH_NAME macro for FDO_DATA.
    //
    pwdtData = WdtGetData(WdfDevice);

    /* Tell the framework that this device will need
       an interface.  */
	status = WdfDeviceCreateDeviceInterface(
		WdfDevice,
		&GUID_DEVINTERFACE_WDT,
		NULL
		);
	if ( !NT_SUCCESS(status) )
	{
		KdPrint(("%s-WDTEvtDeviceAdd: WdfDeviceCreateDeviceInterface "
		         "failed.\n", DRIVERNAME));
		return status;
	}


    /* Configure the IO buffer queue  */
	WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(
		&queueConfig,
        WdfIoQueueDispatchSequential
		);

	/* deliver I/O requests only if device is in working (D0) state */
	queueConfig.PowerManaged = WdfTrue;

	/* ioctl control callback function  */
	queueConfig.EvtIoDeviceControl = WdtIoDeviceControl;

	status = WdfIoQueueCreate(
		WdfDevice,
		&queueConfig,
		WDF_NO_OBJECT_ATTRIBUTES,
		NULL
		);
	if ( !NT_SUCCESS(status) )
	{
		KdPrint(("%s-WDTEvtDeviceAdd: CreateQueue failed.\n", DRIVERNAME));
		return status;
	}

    /* Create a DPC */
    WDF_DPC_CONFIG_INIT(&dpcConfig, WdtDpcInterruptRoutine);
    dpcConfig.AutomaticSerialization = TRUE;

    WDF_OBJECT_ATTRIBUTES_INIT(&dpcAttributes);
    dpcAttributes.ParentObject = WdfDevice;

    status = WdfDpcCreate(&dpcConfig,
                          &dpcAttributes,
                          &pwdtData->IsrDpc);
    if (!NT_SUCCESS(status)) {

        KdPrint(("%s-WDTEvtDeviceAdd: WdfDpcCreate(IsrDpc) failed  [%#08lx]\n",
                 DRIVERNAME, status));
        return status;
    }

	/* store DriverObj, PortBase and IRQ in device context */
	pwdtData->DriverObject = DriverObj;
	pwdtData->PortBase = PortBase;
	pwdtData->IRQ = IRQ;

    KdPrint(("%s-WDTEvtDeviceAdd: Done.\n", DRIVERNAME));

	return status;
}


/*
* Routine Description:
*
*    EvtDevicePrepareHardware callback will configure the driver resources
*
* Arguments:
*
*    Device - Handle to a framework device object.
*
*    Resources - raw (bus-relative) hardware resources that have been
*				assigned to the device.
*
*    ResourcesTranslated - translated (system-physical)
*                hardware resources that have been assigned to the device.
*
* Return Value:
*
*    WDF status code.
*/
NTSTATUS
  WdtEvtDevicePrepareHardware(
    IN WDFDEVICE  Device,
    IN WDFCMRESLIST  ResourcesRaw,
    IN WDFCMRESLIST  ResourcesTranslated)
{
	PDEVICE_EXTENSION pwdtData = NULL;
	NTSTATUS status = STATUS_SUCCESS;

	pwdtData = WdtGetData(Device);

	/* initialize interrupt */

    /* first clear wdt irqstat */
	clr_intr_status(pwdtData);

    KdPrint(("%s-WdtEvtDevicePrepareHardware: Heading into initialize_interrupt Base =  %x"
             " Int = %x\n", DRIVERNAME, pwdtData->PortBase, pwdtData->IRQ));
    status = initialize_interrupt(pwdtData);
	if (!NT_SUCCESS(status))
	{
		KdPrint(("%s-WdtEvtDevicePrepareHardware: initialize_interrupt "
		         "failed.\n", DRIVERNAME));
		return status;
	}

    KdPrint(("%s-WdtEvtDevicePrepareHardware: Done.\n", DRIVERNAME));

    return status;
}


/*
* Routine Description:
*
*    EvtDeviceReleaseHardware is called when the driver is being closed
*
* Arguments:
*
*    Device - Handle to a driver object.
*
*    ResourcesTranslated - translated (system-physical)
*                hardware resources that have been assigned to the device.
*
* Return Value:
*
*    NTSTATUS
*/
NTSTATUS
  WdtEvtDeviceReleaseHardware(
    IN WDFDEVICE  Device,
    IN WDFCMRESLIST  ResourcesTranslated
    )
{
	PDEVICE_EXTENSION pwdtData = NULL;
	NTSTATUS status = STATUS_SUCCESS;
	BOOLEAN conflict;

	pwdtData = WdtGetData(Device);

	/* disable interrupts */
	ioctl_set_intr_type(pwdtData, WDT_INT_TYPE_DIS);
	/*  release interrupt objects */
	IoDisconnectInterrupt( pwdtData->InterruptObject );
	pwdtData->InterruptObject = NULL;

	/* release event object */
	pwdtData->UserEvent = NULL;

	/* release IoReportResourceForDetection */
	status = IoReportResourceForDetection(
									pwdtData->DriverObject,
									NULL,
									0,
									NULL,
									NULL,
									0,
									&conflict );
	if (!NT_SUCCESS(status))
	{
		KdPrint(("%s-WdtEvtDeviceReleaseHardware: IoReportResourceForDetection"
		          " failed\n", DRIVERNAME));
	}

	KdPrint(("%s-WDTEvtDeviceReleaseHardware: Done.\n", DRIVERNAME));

	return status;
}


/*++
* Routine Description:
*
*   EvtDeviceD0Entry callback is called when the system is about to enter
*   the D0 (working) state
*
* Arguments:
*
*    Device - Handle to a driver object.
*
*    PreviousState - previous power state
*
* Return Value:
*
*    NTSTATUS
--*/
NTSTATUS
  WdtEvtDeviceD0Entry(
    IN WDFDEVICE  Device,
    IN WDF_POWER_DEVICE_STATE  PreviousState
    )
{
	PDEVICE_EXTENSION pwdtData = NULL;

	pwdtData = WdtGetData(Device);

	/* if coming from a low power state */
	if((PreviousState == WdfPowerDeviceD1) ||
	   (PreviousState == WdfPowerDeviceD2) ||
	   (PreviousState == WdfPowerDeviceD3))
	{
		/* restore state of WDT registers */
		KdPrint(("%s-WdtEvtDeviceD0Entry: Restoring WDT register state\n",
		          DRIVERNAME));

		ioctl_set_mode( pwdtData, pwdtData->Mode );
		ioctl_set_prescaler( pwdtData, pwdtData->Scale );
		ioctl_set_external_out( pwdtData, pwdtData->ExtOutState );
		ioctl_set_intr_type( pwdtData, pwdtData->IntType );
		ioctl_set_timeout_1( pwdtData, pwdtData->Timeout1 );
		ioctl_set_timeout_2( pwdtData, pwdtData->Timeout2 );

		if(pwdtData->Enabled)
		{
			ioctl_enable_wdt( pwdtData, TRUE );
		}
		else
		{
			ioctl_enable_wdt( pwdtData, FALSE );
		}

		if(pwdtData->Locked)
		{
			ioctl_lock_wdt( pwdtData );
		}
	}

    KdPrint(("%s-WdtEvtDeviceD0Entry: Done.\n", DRIVERNAME));

    return STATUS_SUCCESS;
}


/*++
* Routine Description:
*
*    WDTEvtDeviceD0Exit is called when the system is going into a
*	low power state
*
* Arguments:
*
*    Device - Handle to a river object.
*
*    TargetState - power state the system is going into.
*
* Return Value:
*
*    NTSTATUS
--*/
NTSTATUS
WdtEvtDeviceD0Exit(
    IN  WDFDEVICE Device,
    IN  WDF_POWER_DEVICE_STATE TargetState
    )
{
	PDEVICE_EXTENSION pwdtData = NULL;

	/* save state of WDT registers */
	KdPrint(("%s-WdtEvtDeviceD0Exit: Saving WDT register state\n", DRIVERNAME));
	pwdtData = WdtGetData(Device);
	pwdtData->Mode = ioctl_get_mode( pwdtData );
	pwdtData->Scale = ioctl_get_prescaler( pwdtData );
	pwdtData->ExtOutState = ioctl_get_external_out( pwdtData );
	pwdtData->IntType = ioctl_get_intr_type( pwdtData );
	pwdtData->Timeout1 = ioctl_get_timeout_1( pwdtData );
	pwdtData->Timeout2 = ioctl_get_timeout_2( pwdtData );
	pwdtData->Enabled = ioctl_get_wdt_enable( pwdtData );
	pwdtData->Locked = get_locked( pwdtData );

    KdPrint(("%s-WdtEvtDeviceD0Exit: Done.\n", DRIVERNAME));

	return STATUS_SUCCESS;
}


/*++
 * WdtDpcInterruptRoutine
 *
 * Routine Description:
 *   This function is called after a stage one timeout has occurred.
 *   This function will signal the user-mode event.
 *
 * Arguments:
 *  @param [IN] Dpc
 *
 * Return Value:
 *    None
--*/
VOID
WdtDpcInterruptRoutine(IN WDFDPC Dpc)
{
    PDEVICE_EXTENSION pwdtData = NULL;

    pwdtData = WdtGetData(WdfDpcGetParentObject(Dpc));
    /* make sure user registered the callback  */
    if(NULL == pwdtData->UserEvent)
    {
		KdPrint(("%s-WdtDpcInterruptRoutine: Error no registered callback\n",
		         DRIVERNAME));
		return;
	}
	/* Signal the user mode application event */
	KeSetEvent( pwdtData->UserEvent, 0, FALSE );
	KdPrint(("%s-WdtDpcInterruptRoutine: KeSetEvent Called\n", DRIVERNAME));

    return;
}


/*++
* Routine Description:
*
*    WDTEvtIoDeviceControl is the IO access point for the driver.
*
* Arguments:
*
*    Queue - Handle to the queue object
*    Request - Handle to a request object.
*
*    OutputBufferLength - length of the request's output buffer,
*                        if an output buffer is available.
*    InputBufferLength - length of the request's input buffer,
*                        if an input buffer is available.
*
*    IoControlCode - the driver-defined or system-defined I/O control code
*                    (IOCTL) that is associated with the request.
* Return Value:
*    VOID
--*/
VOID
WdtIoDeviceControl(
	IN WDFQUEUE Queue,
	IN WDFREQUEST Request,
	IN size_t OutputBufferLength,
	IN size_t InputBufferLength,
	IN ULONG IoControlCode
	)
{
    NTSTATUS status= STATUS_SUCCESS;
    size_t bytesReturned = 0;
 	size_t bufSize;
    PVOID RequestBuffer = NULL;
    PVOID ResponseBuffer = NULL;
    PSAWD_CAPABILITY_OUT_BUFF pSaWdtCap;
    PSAWD_WDTENABLE pWdtEnable;
    PSAWD_TIMEOUT pWdtTimeout;
    PSAWD_DC pWdtCounter;
    PSAWD_TOUTCONFIG pWdtToutConfig;
    PSAWD_TOUT pWdtTout;
    PSAWD_PRESCALER pWdtPreScaler;
    PSAWD_EXTERNALOUT pWdtExtOut;
    PSAWD_IRQTYPE pWdtIrqType;
    PSAWD_UHANDLE pWdtUserHandle;
    PDEVICE_EXTENSION pwdtData;
    WDFDEVICE WdfDevice = WdfIoQueueGetDevice(Queue);


    pwdtData = WdtGetData(WdfDevice);

    //
    // Use WdfRequestRetrieveInputBuffer and WdfRequestRetrieveOutputBuffer
    // to get the request buffers.
    //

    switch (IoControlCode)
    {

		/* Return to the caller the WDT capabilities */
		case IOCTL_GET_CAPABILITIES:
			KdPrint(("%s-WdtEvtIoDeviceControl: IOCTL Get Capabilities!!\n",DRIVERNAME));

			bytesReturned = sizeof(SAWD_CAPABILITY_OUT_BUFF);
			status = WdfRequestRetrieveOutputBuffer(Request,
												 bytesReturned,
												 &ResponseBuffer,
												 &bufSize);
			if(!NT_SUCCESS(status))
			{
			  KdPrint(("%s-WdtEvtIoDeviceControl: WdfRequestRetrieveOutputBuffer "
							"failed\n", DRIVERNAME));
			  break;
			}

	        ASSERT(bufSize == OutputBufferLength);

			/* Get pointer to the output buffer, cast to Win32 structure. */
			pSaWdtCap = (PSAWD_CAPABILITY_OUT_BUFF)ResponseBuffer;

			/* Zero out the buffer memory */
			RtlZeroMemory(pSaWdtCap, bytesReturned);

			/* The version number is the number of the single bit set in the
			unsigned long plus one. Exactly one bit must be set. */
			pSaWdtCap->Version = VERSION;
			/* Capabilities supported */
			pSaWdtCap->Capability = NO_DISABLABLE | N0_SHUTDOWNABLE;
			/* smallest timer interval supported by the watchdog timer (ms) */
			pSaWdtCap->min     = MIN_TIMER_INTERVAL;
			/* largest timer interval supported by the watchdog timer (ms) */
			pSaWdtCap->max     = MAX_TIMER_INTERVAL; /* Max  20 bit count value */

			status = STATUS_SUCCESS;
			break;


		/* Enable the Watch dog timer */
		case IOCTL_ENABLE_WDT:
			KdPrint(("%s-WdtEvtIoDeviceControl: IOCTL Enable the WDT!!\n", DRIVERNAME));

			ioctl_enable_wdt(pwdtData, TRUE);

			status = STATUS_SUCCESS;
			break;

		/* Disable the Watch dog timer */
		case IOCTL_DISABLE_WDT:
			KdPrint(("%s-WdtEvtIoDeviceControl: IOCTL Disable the WDT!!\n", DRIVERNAME));

			ioctl_enable_wdt(pwdtData, FALSE);

			status = STATUS_SUCCESS;
			break;

		/* Get the WDT Timer Enable */
		case IOCTL_GET_WDT_ENABLE:
			KdPrint(("%s-WdtEvtIoDeviceControl: IOCTL Get the Watchdog Timer "
			         "Enable!!\n", DRIVERNAME));

			bytesReturned = sizeof(SAWD_WDTENABLE);
			status = WdfRequestRetrieveOutputBuffer(Request,
												 bytesReturned,
												 &ResponseBuffer,
												 &bufSize);
			if(!NT_SUCCESS(status)){
				KdPrint(("%s-WdtEvtIoDeviceControl: WdfRequestRetrieveOutputBuffer "
							"failed\n", DRIVERNAME));
				break;
			}

	        ASSERT(bufSize == OutputBufferLength);

			pWdtEnable = (PSAWD_WDTENABLE)ResponseBuffer;

			pWdtEnable->State = ioctl_get_wdt_enable(pwdtData);

			status = STATUS_SUCCESS;
			break;

		/* Lock the WDT device */
		case IOCTL_LOCK_DEVICE:
			KdPrint( ("%s-WdtEvtIoDeviceControl: IOCTL Writing to the Lock register!!\n", DRIVERNAME) );

			/* Set the Lock Bit in the WDT Lock Register */
			ioctl_lock_wdt(pwdtData);

			status = STATUS_SUCCESS;
			break;

		/* Load the preload 1 counter value */
		case IOCTL_SET_TIMEOUT_1:
			status = WdfRequestRetrieveInputBuffer(Request,
												 sizeof(SAWD_TIMEOUT),
												 &RequestBuffer,
												 &bufSize);
			if(!NT_SUCCESS(status)){
				KdPrint(("%s-WdtEvtIoDeviceControl: WdfRequestRetrieveInputBuffer "
							"failed\n", DRIVERNAME));
				break;
			}

	        ASSERT(bufSize == InputBufferLength);

			pWdtTimeout = (PSAWD_TIMEOUT)RequestBuffer;

			KdPrint(("%s-WdtEvtIoDeviceControl: IOCTL Set the Watchdog Timer "
			         "Preload #1: %X!!\n", DRIVERNAME, pWdtTimeout->Timeout));

			ioctl_set_timeout_1(pwdtData, pWdtTimeout->Timeout);

			status = STATUS_SUCCESS;
			break;

		/* Load the preload 2 counter value */
		case IOCTL_SET_TIMEOUT_2:
			status = WdfRequestRetrieveInputBuffer(Request,
												 sizeof(SAWD_TIMEOUT),
												 &RequestBuffer,
												 &bufSize);
			if(!NT_SUCCESS(status)){
				KdPrint(("%s-WdtEvtIoDeviceControl: WdfRequestRetrieveInputBuffer "
							"failed\n", DRIVERNAME));
				break;
			}

	        ASSERT(bufSize == InputBufferLength);

			pWdtTimeout = (PSAWD_TIMEOUT)RequestBuffer;

			KdPrint(("%s-WdtEvtIoDeviceControl: IOCTL Set the Watchdog Timer "
			         "Preload #2: %X!!\n", DRIVERNAME, pWdtTimeout->Timeout));

			ioctl_set_timeout_2(pwdtData, pWdtTimeout->Timeout);

			status = STATUS_SUCCESS;
			break;

		/* Get the preload 1 counter value */
		case IOCTL_GET_TIMEOUT_1:
			KdPrint(("%s-WdtEvtIoDeviceControl: IOCTL Get the Watchdog Timer "
			         "Preload #1!!\n", DRIVERNAME));

			bytesReturned = sizeof(SAWD_TIMEOUT);
			status = WdfRequestRetrieveOutputBuffer(Request,
												 bytesReturned,
												 &ResponseBuffer,
												 &bufSize);
			if(!NT_SUCCESS(status)){
				KdPrint(("%s-WdtEvtIoDeviceControl: WdfRequestRetrieveOutputBuffer "
							"failed\n", DRIVERNAME));
				break;
			}

	        ASSERT(bufSize == OutputBufferLength);

			pWdtTimeout = (PSAWD_TIMEOUT)ResponseBuffer;

			pWdtTimeout->Timeout = ioctl_get_timeout_1(pwdtData);

			status = STATUS_SUCCESS;
			break;

		/* Get the preload 2 counter value */
		case IOCTL_GET_TIMEOUT_2:
			KdPrint(("%s-WdtEvtIoDeviceControl: IOCTL Get the Watchdog Timer "
                     "Preload #2!!\n", DRIVERNAME));

			bytesReturned = sizeof(SAWD_TIMEOUT);
			status = WdfRequestRetrieveOutputBuffer(Request,
												 bytesReturned,
												 &ResponseBuffer,
												 &bufSize);
			if(!NT_SUCCESS(status)){
				KdPrint(("%s-WdtEvtIoDeviceControl: WdfRequestRetrieveOutputBuffer "
							"failed\n", DRIVERNAME));
				break;
			}

	        ASSERT(bufSize == OutputBufferLength);

			pWdtTimeout = (PSAWD_TIMEOUT)ResponseBuffer;

			pWdtTimeout->Timeout = ioctl_get_timeout_2(pwdtData);

			status = STATUS_SUCCESS;
			break;

		/* Reload watchdog timer from the preload regs to prevent timeout */
		case IOCTL_PING_THE_WDT:
			KdPrint(("%s-WdtEvtIoDeviceControl: IOCTL Reloading the Watchdog Down Counter!!\n", DRIVERNAME) );

			/* Ping the WDT */
			ioctl_ping_wdt(pwdtData);

			status = STATUS_SUCCESS;
			break;

		/* Read the  down counter (we only get the 20 bits on HR) */
		case IOCTL_GET_DOWN_COUNTER:
			KdPrint(("%s-WdtEvtIoDeviceControl: IOCTL Get the Watchdog Down Counter!!\n", DRIVERNAME) );

			bytesReturned = sizeof(SAWD_DC);
			status = WdfRequestRetrieveOutputBuffer(Request,
											 bytesReturned,
											 &ResponseBuffer,
											 &bufSize);
			if(!NT_SUCCESS(status)){
			KdPrint(("%s-WdtEvtIoDeviceControl: WdfRequestRetrieveOutputBuffer "
						"failed\n", DRIVERNAME));
			break;
			}

	        ASSERT(bufSize == OutputBufferLength);

			pWdtCounter = (PSAWD_DC)ResponseBuffer;
			pWdtCounter->Counter = ioctl_get_down_counter(pwdtData);

			status    = STATUS_SUCCESS;
			break;

		/* Set to Watchdog timer mode or free running mode */
		case IOCTL_SET_MODE:
			KdPrint(("%s-WdtEvtIoDeviceControl: IOCTL Setting WDT operation Mode!!\n", DRIVERNAME));
			status = WdfRequestRetrieveInputBuffer(Request,
												 sizeof(SAWD_TOUTCONFIG),
												 &RequestBuffer,
												 &bufSize);
			if(!NT_SUCCESS(status)){
				KdPrint(("%s-WdtEvtIoDeviceControl: WdfRequestRetrieveInputBuffer "
							"failed\n", DRIVERNAME));
				break;
			}

	        ASSERT(bufSize == InputBufferLength);

			pWdtToutConfig = (PSAWD_TOUTCONFIG)RequestBuffer;

			if(WDT_SUCCESS != ioctl_set_mode(pwdtData, pWdtToutConfig->Mode))
			{
				KdPrint(("%s-WdtEvtIoDeviceControl: ioctl_set_mode "
							"failed\n", DRIVERNAME));
				status = STATUS_INTERNAL_ERROR;
				break;
			}

			status = STATUS_SUCCESS;
			break;

		/* Get Watchdog timer mode or free running mode */
		case IOCTL_GET_MODE:
			KdPrint(("%s-WdtEvtIoDeviceControl: IOCTL Get WDT operation Mode!!\n", DRIVERNAME));

			bytesReturned = sizeof(SAWD_TOUTCONFIG);
			status = WdfRequestRetrieveOutputBuffer(Request,
												 bytesReturned,
												 &ResponseBuffer,
												 &bufSize);
			if(!NT_SUCCESS(status)){
				KdPrint(("%s-WdtEvtIoDeviceControl: WdfRequestRetrieveOutputBuffer "
							"failed\n", DRIVERNAME));
				break;
			}

	        ASSERT(bufSize == OutputBufferLength);

			pWdtToutConfig = (PSAWD_TOUTCONFIG)ResponseBuffer;

			pWdtToutConfig->Mode = ioctl_get_mode(pwdtData);

			status = STATUS_SUCCESS;
			break;

		/* Get Watchdog Timeout status */
		case IOCTL_GET_TIMEOUT:
			KdPrint(("%s-WdtEvtIoDeviceControl: IOCTL Get WDT timeout!!\n", DRIVERNAME));

			bytesReturned = sizeof(SAWD_TOUT);
			status = WdfRequestRetrieveOutputBuffer(Request,
												 bytesReturned,
												 &ResponseBuffer,
												 &bufSize);
			if(!NT_SUCCESS(status)){
				KdPrint(("%s-WdtEvtIoDeviceControl: WdfRequestRetrieveOutputBuffer "
							"failed\n", DRIVERNAME));
				break;
			}

	        ASSERT(bufSize == OutputBufferLength);

			pWdtTout = (PSAWD_TOUT)ResponseBuffer;

			pWdtTout->State = ioctl_get_timeout(pwdtData);

			status = STATUS_SUCCESS;
			break;

		/* Clear Watchdog Timeout status */
		case IOCTL_CLR_TIMEOUT:
			KdPrint(("%s-WdtEvtIoDeviceControl: IOCTL Clear WDT timeout!!\n", DRIVERNAME));

			bytesReturned = sizeof(SAWD_TOUT);
			status = WdfRequestRetrieveOutputBuffer(Request,
												 bytesReturned,
												 &ResponseBuffer,
												 &bufSize);
			if(!NT_SUCCESS(status)){
				KdPrint(("%s-WdtEvtIoDeviceControl: WdfRequestRetrieveOutputBuffer "
							"failed\n", DRIVERNAME));
				break;
			}

	        ASSERT(bufSize == OutputBufferLength);

			pWdtTout = (PSAWD_TOUT)ResponseBuffer;

			pWdtTout->State = ioctl_clr_timeout(pwdtData);

			status = STATUS_SUCCESS;
			break;

		/* Set the Watchdog PRESCALER value */
		case IOCTL_SET_PRESCALER:
			KdPrint(("%s-WdtEvtIoDeviceControl: IOCTL Setting Prescaler value!!\n", DRIVERNAME));
			status = WdfRequestRetrieveInputBuffer(Request,
												 sizeof(SAWD_PRESCALER),
												 &RequestBuffer,
												 &bufSize);
			if(!NT_SUCCESS(status)){
				KdPrint(("%s-WdtEvtIoDeviceControl: WdfRequestRetrieveInputBuffer "
							"failed\n", DRIVERNAME));
				break;
			}

	        ASSERT(bufSize == InputBufferLength);

			pWdtPreScaler = (PSAWD_PRESCALER)RequestBuffer;

			/* Set the prescaler bits */
			if(WDT_SUCCESS != ioctl_set_prescaler(pwdtData, pWdtPreScaler->Scale))
			{
				KdPrint(("%s-WdtEvtIoDeviceControl: ioctl_set_prescaler "
							"failed\n", DRIVERNAME));
				status = STATUS_INTERNAL_ERROR;
				break;
			}

			status = STATUS_SUCCESS;
			break;

		/* Get the Watchdog PRESCALER value */
		case IOCTL_GET_PRESCALER:
			KdPrint(("%s-WdtEvtIoDeviceControl: IOCTL Get Prescaler value!!\n", DRIVERNAME));

			bytesReturned = sizeof(SAWD_PRESCALER);
			status = WdfRequestRetrieveOutputBuffer(Request,
												 bytesReturned,
												 &ResponseBuffer,
												 &bufSize);
			if(!NT_SUCCESS(status)){
				KdPrint(("%s-WdtEvtIoDeviceControl: WdfRequestRetrieveOutputBuffer "
							"failed\n", DRIVERNAME));
				break;
			}

	        ASSERT(bufSize == OutputBufferLength);

			pWdtPreScaler = (PSAWD_PRESCALER)ResponseBuffer;

			pWdtPreScaler->Scale = ioctl_get_prescaler(pwdtData);

			status = STATUS_SUCCESS;
			break;

		/* Enable or disable the WDT_TOUT# pin */
		case IOCTL_SET_EXTERNAL_OUT:
			KdPrint(("%s-WdtEvtIoDeviceControl: IOCTL Setting External Out value!!\n", DRIVERNAME));

			status = WdfRequestRetrieveInputBuffer(Request,
												 sizeof(SAWD_EXTERNALOUT),
												 &RequestBuffer,
												 &bufSize);
			if(!NT_SUCCESS(status)){
				KdPrint(("%s-WdtEvtIoDeviceControl: WdfRequestRetrieveInputBuffer "
							"failed\n", DRIVERNAME));
				break;
			}

	        ASSERT(bufSize == InputBufferLength);

			pWdtExtOut = (PSAWD_EXTERNALOUT)RequestBuffer;

			/* Write to the configuration register */
			if(WDT_SUCCESS != ioctl_set_external_out(pwdtData, pWdtExtOut->State))
			{
				KdPrint(("%s-WdtEvtIoDeviceControl: ioctl_set_external_out "
							"failed\n", DRIVERNAME));
				status = STATUS_INTERNAL_ERROR;
				break;
			}

			status = STATUS_SUCCESS;
			break;


		/* Get the Watchdog External Out value */
		case IOCTL_GET_EXTERNAL_OUT:
			KdPrint(("%s-WdtEvtIoDeviceControl: IOCTL Get External Out value!!\n", DRIVERNAME));

			bytesReturned = sizeof(SAWD_EXTERNALOUT);
			status = WdfRequestRetrieveOutputBuffer(Request,
												 bytesReturned,
												 &ResponseBuffer,
												 &bufSize);
			if(!NT_SUCCESS(status)){
				KdPrint(("%s-WdtEvtIoDeviceControl: WdfRequestRetrieveOutputBuffer "
							"failed\n", DRIVERNAME));
				break;
			}

	        ASSERT(bufSize == OutputBufferLength);

			pWdtExtOut = (PSAWD_EXTERNALOUT)ResponseBuffer;

			pWdtExtOut->State = ioctl_get_external_out(pwdtData);

			status = STATUS_SUCCESS;
			break;

		/* Set the interrupt type */
		case IOCTL_SET_INTERRUPT_TYPE:
			KdPrint(("%s-WdtEvtIoDeviceControl: IOCTL Setting Interrupt type!!\n", DRIVERNAME));

			status = WdfRequestRetrieveInputBuffer(Request,
												 sizeof(SAWD_IRQTYPE),
												 &RequestBuffer,
												 &bufSize);
			if(!NT_SUCCESS(status)){
				KdPrint(("%s-WdtEvtIoDeviceControl: WdfRequestRetrieveInputBuffer "
							"failed\n", DRIVERNAME));
				break;
			}

	        ASSERT(bufSize == InputBufferLength);

			pWdtIrqType = (PSAWD_IRQTYPE)RequestBuffer;

			/* Write to the configuration register */
			if(WDT_SUCCESS != ioctl_set_intr_type(pwdtData, pWdtIrqType->Type))
			{
				KdPrint(("%s-WdtEvtIoDeviceControl: ioctl_set_intr_type "
							"failed\n", DRIVERNAME));
				status = STATUS_INTERNAL_ERROR;
				break;
			}

			status = STATUS_SUCCESS;
			break;


		/* Get the interrupt type */
		case IOCTL_GET_INTERRUPT_TYPE:
			KdPrint(("%s-WdtEvtIoDeviceControl: IOCTL Get Interrupt type!!\n", DRIVERNAME));

			bytesReturned = sizeof(SAWD_IRQTYPE);
			status = WdfRequestRetrieveOutputBuffer(Request,
												 bytesReturned,
												 &ResponseBuffer,
												 &bufSize);
			if(!NT_SUCCESS(status)){
				KdPrint(("%s-WdtEvtIoDeviceControl: WdfRequestRetrieveOutputBuffer "
							"failed\n", DRIVERNAME));
				break;
			}

	        ASSERT(bufSize == OutputBufferLength);

			pWdtIrqType = (PSAWD_IRQTYPE)ResponseBuffer;

			pWdtIrqType->Type = ioctl_get_intr_type(pwdtData);

			status = STATUS_SUCCESS;
			break;

		/*
		 * Obtain a kernel pointer to the user-mode-created event handle.
		 * (This IOCTL must be called before the user mode waiting thread is
		 * created)
		 */
		case IOCTL_USER_HANDLE:
			KdPrint( ("%s-WdtEvtIoDeviceControl: IOCTL Getting R0 handle for R3 Event!!\n") );

			bytesReturned = sizeof(SAWD_UHANDLE);
			status = WdfRequestRetrieveInputBuffer(Request,
												 bytesReturned,
												 &RequestBuffer,
												 &bufSize);
			if(!NT_SUCCESS(status)){
				KdPrint(("%s-WdtEvtIoDeviceControl: WdfRequestRetrieveInputBuffer "
							"failed\n", DRIVERNAME));
				break;
			}

	        ASSERT(bufSize == InputBufferLength);

			pWdtUserHandle = (PSAWD_UHANDLE)RequestBuffer;

			if(NULL != pwdtData->UserEvent)
			{
				/* UserEvent was already created so need to delete it */
				pwdtData->UserEvent = NULL;
			}

			status = ObReferenceObjectByHandle(pWdtUserHandle->UserEvent,
									EVENT_MODIFY_STATE,
									(POBJECT_TYPE) NULL,
									KernelMode,
									(PVOID)&pwdtData->UserEvent,
									(POBJECT_HANDLE_INFORMATION) NULL);
			if(status == STATUS_SUCCESS)
			{
				/*  Only needed the event object involved, not a reference to the event. */
				ObDereferenceObject( pwdtData->UserEvent );
				KdPrint( ("%s-WdtEvtIoDeviceControl: IOCTL ObReferenceObjectByHandle SUCCESS!!\n", DRIVERNAME));
			}
			else
			{
				KdPrint( ("%s-WdtEvtIoDeviceControl: IOCTL ObReferenceObjectByHandle FAILURE!!\n", DRIVERNAME));
				break;
			}
			break;

		/*
		 * IOCTL used for testing the EVENT Handling code and to verify that
		 * ring 3 application thread is released when the shared UserEvent
		 * is signaled by the kernel mode driver.  In the Future this will
		 * handled by INT handler
		 */
		case IOCTL_TEST_CALLBACK:
			KdPrint(("%s-WdtEvtIoDeviceControl: IOCTL Signaling User"
			         " Event!!\n", DRIVERNAME));
			if(NULL == pwdtData->UserEvent)
			{
				KdPrint(("%s-WdtEvtIoDeviceControl: Error no registered"
				         " callback\n",	DRIVERNAME));
				status = STATUS_INTERNAL_ERROR;
				break;
			}
			KeSetEvent(pwdtData->UserEvent, 0, FALSE);
		    status = STATUS_SUCCESS;
			break;

		default:
			KdPrint(("%s-WdtEvtIoDeviceControl: Error Invalid Device"
			         " Request\n", DRIVERNAME));
			status = STATUS_INVALID_DEVICE_REQUEST;
    }

    //
    // Complete the Request.
    //
    WdfRequestCompleteWithInformation(Request, status, bytesReturned);
}


#pragma warning(push)

/* warning 4996 is use of deprecated function this is required until WDT
 * is updated*/
#pragma warning (disable: 4996)


/*!
 * get_wdt_config_info_from_lpc() retrieves the WDT base address and interrupt
 *
 * Retrieves the LPC base address and interrupt from the WDT.  This function
 * does no parameter checking.
 *
 * This function will also enable the WDT in the CONFIGURATION space.  Note
 * that this is different from enabling the WDT using its LOCK_REGISTER.
 *
 * @param BaseAddr [OUT] will contain the port I/O address of WDT
 * @param IntNum   [OUT] contains the interrupt number
 *
 * @return The STATUS_SUCCESS on succes, STATUS_UNSUCCESSFUL on failure
 */


NTSTATUS get_wdt_config_info_from_lpc( USHORT *BaseAddr, UCHAR *IntNum )
{
    ULONG          result;
    PCI_COMMON_CONFIG  pci_info;
    PCI_SLOT_NUMBER    slotNum;


    /* Make sure that this system has the right LPC controller */
    slotNum.u.bits.Reserved     = 0;
    slotNum.u.bits.DeviceNumber   = LPC_DEVICE_NUM;
    slotNum.u.bits.FunctionNumber = LPC_FUNCTION_NUM;

    result = HalGetBusData( PCIConfiguration, LPC_BUS_NUM, slotNum.u.AsULONG,
                            &pci_info, sizeof(PCI_COMMON_CONFIG));

    /* If we cannot find the device or if the device ID is not ours, then
     * quit
     */
    if ( (result == 0) || (LPC_DEVICE_ID != pci_info.DeviceID) ) {
        return STATUS_UNSUCCESSFUL;
    }

    KdPrint(("%s-get_wdt_config_info_from_lpc: Port Index: %x, Data %x\n",
             DRIVERNAME, CONFIG_INDEX, CONFIG_DATA) );

    /* Enter the configuration mode */
    WRITE_PORT_UCHAR( (PUCHAR)CONFIG_INDEX, CONFIG_UNLOCK_KEY1 );
    WRITE_PORT_UCHAR( (PUCHAR)CONFIG_INDEX, CONFIG_UNLOCK_KEY2 );

    /* Set logical device number register to 7 so that we can access the WDT-
     * related registers */
    WRITE_PORT_UCHAR( (PUCHAR) CONFIG_INDEX, CONFIG_GLB_LOGIC_DEV_SELECT_REG );
    WRITE_PORT_UCHAR( (PUCHAR) CONFIG_DATA, WDT_LOGICAL_DEV_NUM );

    /* Get base address */
    WRITE_PORT_UCHAR( (PUCHAR)CONFIG_INDEX, CONFIG_WDT_BASE_ADDR_MSB_REG );
    *BaseAddr = READ_PORT_UCHAR( (PUCHAR)CONFIG_DATA ) << INT_8;

    WRITE_PORT_UCHAR( (PUCHAR)CONFIG_INDEX, CONFIG_WDT_BASE_ADDR_LSB_REG );
    *BaseAddr |= READ_PORT_UCHAR( (PUCHAR)CONFIG_DATA );

    /* Get interrupt */
    WRITE_PORT_UCHAR( (PUCHAR)CONFIG_INDEX, CONFIG_WDT_INT_SELECT_REG );
    *IntNum = READ_PORT_UCHAR( (PUCHAR)CONFIG_DATA );

    /* Enable device */
    WRITE_PORT_UCHAR( (PUCHAR)CONFIG_INDEX, CONFIG_WDT_ENABLE_REG );
    WRITE_PORT_UCHAR( (PUCHAR)CONFIG_DATA, 1 );           /* 1 to enable */

    /* Exit the configuration mode */
    WRITE_PORT_UCHAR( (PUCHAR)CONFIG_INDEX, CONFIG_LOCK_KEY1 );
    WRITE_PORT_UCHAR( (PUCHAR)CONFIG_INDEX,  CONFIG_LOCK_KEY2 );

    return STATUS_SUCCESS;
}


/*!
 * clr_intr_status() clears Interupt Status register
 *
 * Clears the 'Interrupt Status Register'.
 *
 * @param [IN] DevExt device extension, need this to get hardware information
 *
 * @return The STATUS_SUCCESS on succes, STATUS_UNSUCCESSFUL on failure
 */

NTSTATUS clr_intr_status( PDEVICE_EXTENSION DevExt )
{
    PUCHAR  port_base = (PUCHAR) DevExt->PortBase;
    UCHAR   value = 0;

    /* Grab the contents of the General Interrupt Status register.
    Only need bits 0, 1, and 2*/
    value = READ_PORT_UCHAR( port_base + WDT_INT_STATUS_REG_OFFSET );

    /* write 1 to the bit 0:1:2 to clear the intrrupt active bits */
    value |= IRQ_TEST;

    WRITE_PORT_UCHAR( port_base + WDT_INT_STATUS_REG_OFFSET, value );

    return STATUS_SUCCESS;
}


/*!
 * get_locked() get WDT Lock bit
 *
 * This function will get the WDT Timer Lock bit in the WDT lock register.
 *
 * @param [IN] DevExt device extension, need this to get hardware information
 *
 * @return TRUE Locked or FALSE unlocked
 */

BOOLEAN get_locked( PDEVICE_EXTENSION DevExt )
{
    PUCHAR  port_base = (PUCHAR) DevExt->PortBase;
    UCHAR   value = 0;


	/* Read current register state */
	value = READ_PORT_UCHAR(port_base + WDT_LOCK_REGISTER_OFFSET);
	if (0 == (value & LOCK_TEST))
	{
		return FALSE;
	}
	return TRUE;
}


/*!
 * initialize_interrupt() registers WDT's interrupt with the OS
 *
 * Calls IoConnectInterrupt() to connect the interrupt.
 *
 * @param DevObj [INOUT] passed into IoConnectInterrupt.  Need the device
 *                 extension information
 *
 * @return The STATUS_SUCCESS on succes, STATUS_UNSUCCESSFUL on failure
 */

NTSTATUS initialize_interrupt( PDEVICE_EXTENSION pde )
{
    KAFFINITY     affinity;
    ULONG         mapped_vector;
    NTSTATUS      status;
    KIRQL         irql;

    mapped_vector = HalGetInterruptVector(
                                Isa,
                                0,
                                pde->IRQ,
                                pde->IRQ,
                                &irql,
                                &affinity );

    if( 0 == mapped_vector )
    {
        KdPrint( ("HalGetInterruptVector() failed.") );
        return STATUS_UNSUCCESSFUL;
    }


    status = IoConnectInterrupt(
                        &pde->InterruptObject,     /* InterruptObject */
                        isr,                       /* ServiceRoutine */
                        pde,                       /* ServiceContext */
                        NULL,                      /* SpinLock */
                        mapped_vector,             /* Vector */
                        irql,                      /* Irql */
                        irql,                      /* SynchronizeIrql */
                        Latched,                   /* InterruptMode */
                        FALSE,                     /* ShareVector */
                        affinity,                  /* ProcessorEnableMask */
                        FALSE );                   /* FloatingSave */

    return status;
}

#pragma warning(pop)


/*!
 * isr() is the interrupt service routine for WDT driver
 *
 * Finds out if the interrupt is from the WDT device.  If it is, then
 * schedule a DPC and return.
 *
 * @param Interrupt [IN] contains information about the interrupt.
 * @param Context [IN] WDT device object
 *
 * @return returns TRUE or FALSE
 */

BOOLEAN isr( PKINTERRUPT Interrupt, PVOID Context )
{
    PDEVICE_EXTENSION pde = Context;
    PUCHAR  port_base     = (PUCHAR) pde->PortBase;
    UCHAR reg_value;

    /* check if the interrupt is for WDT */
    reg_value = READ_PORT_UCHAR(port_base +
                                    WDT_INT_STATUS_REG_OFFSET);
    if(reg_value == 0)
    {
	  return FALSE;
	}

    KdPrint( ("%s-isr: Entered Interrupt Service Routine\n", DRIVERNAME) );

    /* clear the interrupt status reg */
    WRITE_PORT_UCHAR(port_base + WDT_INT_STATUS_REG_OFFSET, 0x07);

    /* Schedules the execution of the DPC object's EvtDpcFunc */
    WdfDpcEnqueue(pde->IsrDpc);

    return TRUE;
}
