 /*
 * 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
 */

/*****************************************************************************
 * Module name:
 *    1588
 *
 * Abstract: 
 *		The 1588 driver module enables a client application to have
 * 		access to the 1588 time synchronization hardware assist block. 
 * 		A client application can use this driver to retreive tx and rx timestamps 
 * 		of IEEE 1588(TM) PTP messages captured on ethernet or CAN ports. 
 * 		Access is provided by a set of APIs that can be called directly or through 
 * 		ioctl commands.  
 *
 * Revision:
 *    
 *
 *****************************************************************************/


#include "1588.h"

//
// Make sure the initialization code is removed from memory after use.
//
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(PAGE, TSEvtDeviceAdd)
#pragma alloc_text(PAGE, TSEvtDevicePrepareHardware)
#pragma alloc_text(PAGE, TSEvtDeviceReleaseHardware)
#pragma alloc_text(PAGE, TSEvtIoDeviceControl)
#pragma alloc_text(PAGE, TSEvtDeviceD0Entry)
#pragma alloc_text(PAGE, TSEvtDeviceD0Exit)
#endif


NTSTATUS
DriverEntry(
	IN PDRIVER_OBJECT DriverObject,
	IN PUNICODE_STRING RegistryPath
	)
/*

Routine Description:

    Driver initialization entry point.

Arguments:

    DriverObject - Pointer to the driver object 

Return Value:

    NTSTATUS

*/
{
	NTSTATUS status = STATUS_SUCCESS;
	WDF_DRIVER_CONFIG config;

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

    //
    // Create a WDFDRIVER object.
    //
	status = WdfDriverCreate(
		DriverObject,
		RegistryPath,
		WDF_NO_OBJECT_ATTRIBUTES,
		&config,
		WDF_NO_HANDLE
		);

	if ( !NT_SUCCESS(status) ) 
	{
		DbgPrint("%s-DriverEntry: WdfDriverCreate failed.\n", DRIVERNAME);
	}

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

	return status;
}


NTSTATUS
TSEvtDeviceAdd(
	IN WDFDRIVER Driver,
	IN PWDFDEVICE_INIT DeviceInit
	)
/*

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 status = STATUS_SUCCESS;
	WDFDEVICE hDevice;
	WDF_IO_QUEUE_CONFIG queueConfig;
	WDF_OBJECT_ATTRIBUTES objAttributes;
	WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
	WDF_INTERRUPT_CONFIG interruptConfig;
    timesync_data_t *ts_data;

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

    //
    // Set WDF Callbacks 
	//

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

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

    //
    // Register the PnP and power callbacks. 
    //
	WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);
	
    //
    // Init the device context 
    //
	WDF_OBJECT_ATTRIBUTES_INIT(&objAttributes);

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

    //
    // Create a framework device object.
    //
	status = WdfDeviceCreate(
		&DeviceInit,
		&objAttributes,
		&hDevice
		);

	if ( !NT_SUCCESS(status) ) 
	{
		DbgPrint("%s-TSEvtDeviceAdd: WdfDeviceCreate failed.\n", DRIVERNAME);
        return status;
	}

    //
    // obtain the device context
    //
    ts_data = WdfObjectGet_timesync_data_t(hDevice);

    //
    // init context fields
    //
    ts_data->pRegs = 0;
    ts_data->WdfDevice = hDevice;

	memset(&ts_data->state, 0, sizeof(reg_states_t));

	//
    // Tell the framework that this device will need
    // an interface.
    //
	status = WdfDeviceCreateDeviceInterface(
		hDevice,
		&GUID_DEVINTERFACE_1588,
		NULL
		);

	if ( !NT_SUCCESS(status) )
	{
		DbgPrint("%s-TSEvtDeviceAdd: WdfDeviceCreateDeviceInterface failed.\n", 
                    DRIVERNAME);
		return status;
	}


	//
	// create the ioctl queue
	//

    //
    // Configure the IO buffer queue 
    //
	     
    // init the queue and allow parallel ioctl call access
	WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(
		&queueConfig,
        WdfIoQueueDispatchParallel
		);

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

	//
	// ioctl control callback function 
	//
	queueConfig.EvtIoDeviceControl = TSEvtIoDeviceControl;

	status = WdfIoQueueCreate(
		hDevice,
		&queueConfig,
		WDF_NO_OBJECT_ATTRIBUTES,
		NULL
		);

	if ( !NT_SUCCESS(status) )
	{
		DbgPrint("%s-TSEvtDeviceAdd: CreateQueue failed.\n", DRIVERNAME);
		return status;
	}


	//
	// request an interrupt
	//

	WDF_INTERRUPT_CONFIG_INIT(
		&interruptConfig, 
		TimeSyncIsr, 
		NULL
		);

    //
    // These two callbacks enable and disable interrupts.
    //
	interruptConfig.EvtInterruptEnable = TSEvtInterruptEnable;
	interruptConfig.EvtInterruptDisable = TSEvtInterruptDisable;

	status = WdfInterruptCreate(
		hDevice,
		&interruptConfig,
		WDF_NO_OBJECT_ATTRIBUTES,
		&(ts_data->Interrupt)
		);

	if ( !NT_SUCCESS(status) )
	{
		DbgPrint("%s-TSEvtDeviceAdd: InterruptCreate failed.\n", DRIVERNAME);
		return status;
	}

    DbgPrint("%s-TSEvtDeviceAdd: complete.\n", DRIVERNAME);

	return status;
}


NTSTATUS
  TSEvtDevicePrepareHardware(
    IN WDFDEVICE  Device,
    IN WDFCMRESLIST  ResourcesRaw,
    IN WDFCMRESLIST  ResourcesTranslated
    )
/*

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.
*/
{
	ULONG i;
	PCM_PARTIAL_RESOURCE_DESCRIPTOR descriptor;
	BOOLEAN bResInterrupt = FALSE;
	BOOLEAN bResMemory = FALSE;
    UINT32 reg_val = 0;
    PDEVICE_OBJECT pdo;   

	timesync_data_t *ts_data = WdfObjectGet_timesync_data_t(Device);

	for (i=0; i < WdfCmResourceListGetCount(ResourcesTranslated); i++) 
	{
		descriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, i);

        if ( !descriptor )
        {
            DbgPrint("%s-TSEvtDevicePrepareHardware: WdfCmResourceListGetDescriptor failed\n", 
                        DRIVERNAME);
            return STATUS_DEVICE_CONFIGURATION_ERROR;
        }

		switch (descriptor->Type) 
		{
			case CmResourceTypeMemory:

                // only get BAR 0
                if ( ts_data->pRegs )
                {
                    break;
                }

                DbgPrint("%s-TSEvtDevicePrepareHardware: Memory start %8X%8.8lX "
                            "length %X\n",
                    DRIVERNAME,
					descriptor->u.Memory.Start.HighPart, 
					descriptor->u.Memory.Start.LowPart,
					descriptor->u.Memory.Length);

                // map the virtual IO space
                ts_data->pRegs = MmMapIoSpace(
							descriptor->u.Memory.Start,
							descriptor->u.Memory.Length,
							MmNonCached);

				if ( !ts_data->pRegs ) 
				{
					DbgPrint("%s-TSEvtDevicePrepareHardware: unable to map "
                                "memory\n", DRIVERNAME);

                    return STATUS_INSUFFICIENT_RESOURCES;
				}

                ts_data->RegSize = descriptor->u.Memory.Length;

                // init the HAL i/o memory addr structure
                ixTimeSyncAccBlPlBaseAddressesSet((ULONG)ts_data->pRegs);

                bResMemory = TRUE;
				break;

			case CmResourceTypeInterrupt:
				DbgPrint("%s-TSEvtDevicePrepareHardware: Interrupt  level %X, " 
                                    "vector %X, affinity %X\n",
                    DRIVERNAME,
					descriptor->u.Interrupt.Level, 
					descriptor->u.Interrupt.Vector,
					descriptor->u.Interrupt.Affinity);

                bResInterrupt = TRUE;
				break;

			default:
				DbgPrint("%s-TSEvtDevicePrepareHardware: Unhandled resource type "
                            "(0x%x)\n", DRIVERNAME, descriptor->Type);
				break;
		}
	}
	            
    // if memory or interrupt resource not 
    if ( !bResMemory || !bResInterrupt )
    {
        if ( !bResMemory )
        {
            DbgPrint("%s-TSEvtDevicePrepareHardware: IO memory not found\n", 
                        DRIVERNAME);
        }

        if ( !bResInterrupt )
        {
            DbgPrint("%s-TSEvtDevicePrepareHardware: Interrupt not found\n", 
                        DRIVERNAME);
        }

        return STATUS_DEVICE_CONFIGURATION_ERROR;
    }             

    // init events
    for (i=0; i < NUM_EVENTS; i++)
    {
        KeInitializeEvent((PRKEVENT)&gNotifyEvent[i], NotificationEvent, FALSE);
    } 

    // init callback ptrs
    TargetTimeCallbackPtr = TargTimeCallback;
    AuxTimeCallbackPtr = AuxTimeCallback;
    AuxTargetTimeCallbackPtr = AuxTargetTimeCallback;
    PulsePerSecondCallbackPtr = PulsePerSecondCallback;

    pdo = WdfDeviceWdmGetDeviceObject(Device);

    // Set interrupts to target the driver 
    reg_val = 0x1;
    PCIConfigReadWrite(pdo, 1, &reg_val, 0xE8, 2);

	return STATUS_SUCCESS;
}


NTSTATUS
  TSEvtDeviceReleaseHardware(
    IN WDFDEVICE  Device,
    IN WDFCMRESLIST  ResourcesTranslated
    )
/*

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 status = STATUS_SUCCESS;

    timesync_data_t *ts_data = WdfObjectGet_timesync_data_t(Device);

	//
    // release the mapped registers
	//
    if ( ts_data->pRegs )
    {
        MmUnmapIoSpace(ts_data->pRegs, ts_data->RegSize);
        ts_data->pRegs = NULL;
    }  
    
	DbgPrint("%s-TSEvtDeviceReleaseHardware: Done.\n", DRIVERNAME);

	return status;
}


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

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

--*/
{
	 PDEVICE_OBJECT pdo; /* IXA00160934 */
 	 UINT32 reg_val = 0; /* IXA00160934 */
	 // if coming from a low power state 
	 if ( (PreviousState == WdfPowerDeviceD1) ||
		 (PreviousState == WdfPowerDeviceD2) )
	 {
        //restore state of 1588 registers
		RestoreRegState(Device);
	 }
	 
	 /* IXA00160934 - 1588 device not seeing interrupts after S3 resume
	 * due to SMIA (0xE8) not being set to 1 by driver.
    */
	 pdo = WdfDeviceWdmGetDeviceObject(Device);
	 // Set interrupts to target the driver
	 reg_val = 0x1;
	 PCIConfigReadWrite(pdo, 1, &reg_val, 0xE8, 2);

    return STATUS_SUCCESS;
}


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

Routine Description:

    TSEvtDeviceD0Exit 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

--*/
{
	// if entering a low power state 
	if ( (TargetState == WdfPowerDeviceD1) ||
		 (TargetState == WdfPowerDeviceD2) )
	{
		//save state of 1588 registers
		SaveRegState(Device);
	}

	return STATUS_SUCCESS;
}


NTSTATUS
  TSEvtInterruptEnable(
    IN WDFINTERRUPT  Interrupt,
    IN WDFDEVICE  AssociatedDevice
    )
/*++

Routine Description:

    TSEvtInterruptEnable is called to enable device interrupts which have
	been disabled

Arguments:

    Interrupt - Handle to an interrupt .

    AssociatedDevice - Handle to a driver object.

Return Value:

    BOOLEAN - TRUE indicates that the interrupt was successfully enabled.

--*/
{
    // !!! save state (elsewhere) when int's are en/dis-abled, restore here?
	
	// restore any previously disabled interrupts 
	RestoreInterrupts(AssociatedDevice);

	return STATUS_SUCCESS;
}


NTSTATUS
  TSEvtInterruptDisable(
    IN WDFINTERRUPT  Interrupt,
    IN WDFDEVICE  AssociatedDevice
    )
/*++

Routine Description:

    TSEvtInterruptDisable is called disable device interrupts 

Arguments:

    Interrupt - Handle to an interrupt .

    AssociatedDevice - Handle to a driver object.

Return Value:

    BOOLEAN - TRUE indicates that the interrupt was successfully disabled.

--*/
{
	// make sure all interrupts are disabled
	DisableInterrupts();

	return STATUS_SUCCESS;
}


BOOLEAN
TimeSyncIsr(
	IN WDFINTERRUPT  Interrupt,
	IN ULONG  MessageID
	)
/*++

Routine Description:

    This is the interrupt service routine for the 1588 driver.
    It will determine whether the 1588 device is the source of this
    interrupt and call the corresponding callback function to process
    the interrupt.  

Arguments:

    InterruptObject - Points to the interrupt object declared for this
    device.  We *do not* use this parameter.


Return Value:

    This function will return TRUE if the 1588 device is the source
    of this interrupt, FALSE otherwise.

--*/
{
    //check intrpt event flags for this device
    if ( !ixTimeSyncAccEventAmmsFlagGet() && !ixTimeSyncAccEventAsmsFlagGet() && 
         !ixTimeSyncAccEventAtmFlagGet() && !ixTimeSyncAccEventPpsmFlagGet() &&
         !ixTimeSyncAccEventTtmFlagGet() )
    {
        return FALSE;
    }

    // process the interrupt in the HAL
	if ( ixTimeSyncAccIsr() != ICP_TIMESYNCACC_SUCCESS )
    {
        DbgPrint("%s-TimeSyncIsr: ixTimeSyncAccIsr() failed\n", DRIVERNAME);
    } 

    return TRUE;
}


VOID
TSEvtIoDeviceControl(
	IN WDFQUEUE Queue,
	IN WDFREQUEST Request,
	IN size_t OutputBufferLength,
	IN size_t InputBufferLength,
	IN ULONG IoControlCode
	)
/*
Routine Description:

    TSEvtIoDeviceControl 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  
  
*/
{
	NTSTATUS Status = STATUS_SUCCESS;
	WDFMEMORY Memory;
	size_t BytesReturned = 0;
    PVOID RequestBuffer = NULL;
    PVOID ParamAddr = NULL;
    PRKEVENT Event=NULL;
    UINT32 val32;
    
    IxTimeSyncAccAuxMode auxMode;
    IxTimeSyncAcc1588PTPPort ptpPort;
    IxTimeSyncAccPortCfgIoctl *pPortCfgIoctl;
    IxTimeSyncAccRxTxPollIoctl *pRxTxPollIoctl;
    IxTimeSyncAccCANPollIoctl *pCANPollIoctl;
    IxTimeSyncAccTimePollIoctl *pTimePollIoctl;
    IxTimeSyncAccTimeValue TimeValue;
    IxTimeSyncAccVersionIoctl *pVersionIoctl;
    IxTimeSyncAccOperationModeIoctl *pOperationModeIoctl;

    timesync_data_t *ts_data = 
        WdfObjectGet_timesync_data_t(WdfIoQueueGetDevice(Queue));

	//
    // Determine which I/O control code was specified.
	//
    switch ( IoControlCode )
    {
        case IOCTL_TARG_TIME_NOTIFY:
        case IOCTL_AUX_TIME_NOTIFY:
        case IOCTL_AUX_TARG_TIME_NOTIFY:
        case IOCTL_PULSE_PER_SEC_NOTIFY:

			// 
			// request to be notified of a 1588 interrupt event
            //

            // Target Time
			if ( IoControlCode == IOCTL_TARG_TIME_NOTIFY )
			{
				Event = (PRKEVENT)&gNotifyEvent[TARG_TIME_EVENT_NUM];
                BytesReturned = sizeof(IxTimeSyncAccTimeValue);
                ParamAddr = &gTargTimeIoctl; 
			}
            // Aux Time                      
			else if ( IoControlCode == IOCTL_AUX_TIME_NOTIFY )
			{
				Event = (PRKEVENT)&gNotifyEvent[AUX_TIME_EVENT_NUM];
                BytesReturned = sizeof(IxTimeSyncAccAuxTimeIoctl);
                ParamAddr = &gAuxTimeIoctl; 
			}
			// Aux Target Time
			else if ( IoControlCode == IOCTL_AUX_TARG_TIME_NOTIFY )
			{
				Event = (PRKEVENT)&gNotifyEvent[AUX_TARG_TIME_EVENT_NUM];
                BytesReturned = sizeof(IxTimeSyncAccTimeValue);
                ParamAddr = &gAuxTargTimeIoctl; 
			}
			// Pulse Per Second
			else if ( IoControlCode == IOCTL_PULSE_PER_SEC_NOTIFY )
			{
				Event = (PRKEVENT)&gNotifyEvent[PPS_EVENT_NUM];
                BytesReturned = sizeof(UINT32);
                ParamAddr = &gPulsePerSecondIoctl; 
			}
                        
			// wait infinitely for a 1588 interrupt event to occur
            KeWaitForSingleObject(Event, Executive, KernelMode, FALSE, NULL);

            // reset event 
            KeResetEvent(Event);
                
            // get buffer to be returned to user
            Status = WdfRequestRetrieveOutputBuffer(Request, 
                                                    BytesReturned, 
                                                    &RequestBuffer, 
                                                    NULL);
	        if ( !NT_SUCCESS(Status) ) 
	        {
                DbgPrint("%s-TSEvtIoDeviceControl: WdfRequestRetrieveInputBuffer "
                            "failed\n", DRIVERNAME);
                break;
            }
            
            // copy event data to return buffer 
            memcpy(RequestBuffer, ParamAddr, BytesReturned);
			break;

        case IOCTL_TARG_TIME_CLR_NOTIFY:
        case IOCTL_AUX_TIME_CLR_NOTIFY:
        case IOCTL_AUX_TARG_TIME_CLR_NOTIFY:
        case IOCTL_PULSE_PER_SEC_CLR_NOTIFY:

			// 
			// request to release a notify thread that is waiting
			// on a 1588 interrupt event 
			//
	                               
			// Target Time
			if ( IoControlCode == IOCTL_TARG_TIME_CLR_NOTIFY )
			{
                Event = (PRKEVENT)&gNotifyEvent[TARG_TIME_EVENT_NUM];
			}
			// Aux Time
			else if ( IoControlCode == IOCTL_AUX_TIME_CLR_NOTIFY )
			{
                Event = (PRKEVENT)&gNotifyEvent[AUX_TIME_EVENT_NUM];
			}
			// Aux Target Time
			else if ( IoControlCode == IOCTL_AUX_TARG_TIME_CLR_NOTIFY )
			{
				Event = (PRKEVENT)&gNotifyEvent[AUX_TARG_TIME_EVENT_NUM];
			}
			// Pulse Per Second
			else if ( IoControlCode == IOCTL_PULSE_PER_SEC_CLR_NOTIFY )
			{
				Event = (PRKEVENT)&gNotifyEvent[PPS_EVENT_NUM];
			}
            
			// force notify thread to wake up
            KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
            // make sure event state is put back to non-signaled 
            KeResetEvent(Event);

			break;
                 
        case IOCTL_TARG_TIME_INTRPT_ENABLE:

			// 
			// request to enable the target time interrupt
			//

            // enable the Target Time interrupt
			if ( ixTimeSyncAccTargetTimeInterruptEnable(TargetTimeCallbackPtr) 
					!= ICP_TIMESYNCACC_SUCCESS )
			{
				DbgPrint("%s-TSEvtIoDeviceControl: "
                            "ixTimeSyncAccTargetTimeInterruptEnable failed\n", 
						     DRIVERNAME);
				Status = STATUS_INVALID_PARAMETER;
			}
			break; 
            
            
        case IOCTL_AUX_TIME_INTRPT_ENABLE:

			// 
			// request to enable the aux time interrupt
			//

            // retrieve parameters from user buffer
            Status = WdfRequestRetrieveInputBuffer(Request, 
                                                   sizeof(IxTimeSyncAccAuxMode), 
                                                   &RequestBuffer, 
                                                   NULL);
	        if ( !NT_SUCCESS(Status) ) 
	        {
                DbgPrint("%s-TSEvtIoDeviceControl: WdfRequestRetrieveInputBuffer "
                            "failed\n", DRIVERNAME);
                break;
            }

            // copy aux mode params from user buffer
            memcpy(&auxMode, RequestBuffer, sizeof(IxTimeSyncAccAuxMode));

            // enable the Aux Time interrupt
			if ( ixTimeSyncAccAuxTimeInterruptEnable(auxMode, AuxTimeCallbackPtr) 
					!= ICP_TIMESYNCACC_SUCCESS )
			{
				DbgPrint("%s-TSEvtIoDeviceControl: "
                                "ixTimeSyncAccAuxTimeInterruptEnable failed\n", 
						         DRIVERNAME);
				Status = STATUS_INVALID_PARAMETER;
			}
			break;
            
        case IOCTL_AUX_TARG_TIME_INTRPT_ENABLE:

			// 
			// request to enable the aux target time interrupt
			//

            // enable the Aux Target Time interrupt
			if ( ixTimeSyncAccAuxTargetTimeInterruptEnable(AuxTargetTimeCallbackPtr) 
					!= ICP_TIMESYNCACC_SUCCESS )
			{
				DbgPrint("%s-TSEvtIoDeviceControl: "
                                "ixTimeSyncAccAuxTargetTimeInterruptEnable failed\n", 
						         DRIVERNAME);
				Status = STATUS_INVALID_PARAMETER;
			}
			break;

        case IOCTL_PULSE_PER_SEC_INTRPT_ENABLE:

			// 
			// request to enable the pulse per second interrupt
			//

            // enable the Pulse per Second interrupt
			if ( ixTimeSyncAccPulsePerSecondInterruptEnable(PulsePerSecondCallbackPtr) 
					!= ICP_TIMESYNCACC_SUCCESS )
			{
				DbgPrint("%s-TSEvtIoDeviceControl: "
                                "ixTimeSyncAccPulsePerSecondInterruptEnable failed\n", 
						         DRIVERNAME);
				Status = STATUS_INVALID_PARAMETER;
			}
			break;
                                  
        case IOCTL_TARG_TIME_INTRPT_DISABLE:

			// 
			// request to disable the target time interrupt
			//

            // disable the Target Time interrupt
			if ( ixTimeSyncAccTargetTimeInterruptDisable() 
					!= ICP_TIMESYNCACC_SUCCESS )
			{
				DbgPrint("%s-TSEvtIoDeviceControl: "
                                "ixTimeSyncAccTargetTimeInterruptDisable failed\n", 
						         DRIVERNAME);
				Status = STATUS_INVALID_PARAMETER;
			}
			break;

        case IOCTL_AUX_TIME_INTRPT_DISABLE:

			// 
			// request to disable the aux time interrupt
			//

            // retreive parameters from user buffer 
            Status = WdfRequestRetrieveInputBuffer(Request, 
                                                   sizeof(IxTimeSyncAccAuxMode), 
                                                   &RequestBuffer, 
                                                   NULL);
	        if ( !NT_SUCCESS(Status) ) 
	        {
                DbgPrint("%s-TSEvtIoDeviceControl: WdfRequestRetrieveInputBuffer "
                            "failed\n", DRIVERNAME);
                break;
            }

            // copy aux mode params from user buffer
            memcpy(&auxMode, RequestBuffer, sizeof(IxTimeSyncAccAuxMode));

            // disable the Aux Time interrupt
			if ( ixTimeSyncAccAuxTimeInterruptDisable(auxMode) 
					!= ICP_TIMESYNCACC_SUCCESS )
			{
				DbgPrint("%s-TSEvtIoDeviceControl: "
                                "ixTimeSyncAccAuxTimeInterruptDisable failed\n", 
						         DRIVERNAME);     
				Status = STATUS_INVALID_PARAMETER;
			}
			break;

        case IOCTL_AUX_TARG_TIME_INTRPT_DISABLE:

			// 
			// request to disable the aux target time interrupt
			//

            // disable the Aux Target Time interrupt
			if ( ixTimeSyncAccAuxTargetTimeInterruptDisable() 
					!= ICP_TIMESYNCACC_SUCCESS )
			{
				DbgPrint("%s-TSEvtIoDeviceControl: "
                                "ixTimeSyncAccAuxTargetTimeInterruptDisable failed\n", 
						         DRIVERNAME);
				Status = STATUS_INVALID_PARAMETER;
			}
			break;

        case IOCTL_PULSE_PER_SEC_INTRPT_DISABLE:

			// 
			// request to disable the pulse per second interrupt
			//

            // disable the Pulse per Second interrupt
			if ( ixTimeSyncAccPulsePerSecondInterruptDisable() 
					!= ICP_TIMESYNCACC_SUCCESS )
			{
				DbgPrint("%s-TSEvtIoDeviceControl: "
                                "ixTimeSyncAccPulsePerSecondInterruptDisable failed\n", 
						         DRIVERNAME);
				Status = STATUS_INVALID_PARAMETER;
			}
			break;

        case IOCTL_PORT_CONFIG_SET:

			// 
			// request to configure port master/slave/all settings
			//

            // retreive parameters from user buffer 
            Status = WdfRequestRetrieveInputBuffer(Request, 
                                                sizeof(IxTimeSyncAccPortCfgIoctl), 
                                                   &RequestBuffer, 
                                                   NULL);
	        if ( !NT_SUCCESS(Status) ) 
	        {
                DbgPrint("%s-TSEvtIoDeviceControl: WdfRequestRetrieveInputBuffer "
                                "failed\n", DRIVERNAME);
                break;
            }

            // cast user buff to port cfg struct ptr 
            pPortCfgIoctl = (IxTimeSyncAccPortCfgIoctl*)RequestBuffer; 

            // configure the port
			if ( ixTimeSyncAccPTPPortConfigSet(pPortCfgIoctl->ptpPort, 
                                             pPortCfgIoctl->ptpPortMode) 
					!= ICP_TIMESYNCACC_SUCCESS )
			{
				DbgPrint("%s-TSEvtIoDeviceControl: "
                                "ixTimeSyncAccPTPPortConfigSet failed\n", 
						         DRIVERNAME);
				Status = STATUS_INVALID_PARAMETER;
			}
			break;

        case IOCTL_PORT_CONFIG_GET:

			// 
			// request to retrieve port master/slave/all settings
			//

            // retreive user buffer to return parameters 
            Status = WdfRequestRetrieveInputBuffer(Request, 
                                               sizeof(IxTimeSyncAccPortCfgIoctl), 
                                                   &RequestBuffer, 
                                                   NULL);
	        if ( !NT_SUCCESS(Status) ) 
	        {
                DbgPrint("%s-TSEvtIoDeviceControl: WdfRequestRetrieveInputBuffer "
                                "failed\n", DRIVERNAME);
                break;
            }

            // cast user buff to port cfg struct ptr 
            pPortCfgIoctl = (IxTimeSyncAccPortCfgIoctl*)RequestBuffer; 

            // get port config data
			if ( ixTimeSyncAccPTPPortConfigGet(pPortCfgIoctl->ptpPort, 
                                             &pPortCfgIoctl->ptpPortMode) 
					!= ICP_TIMESYNCACC_SUCCESS )
			{
				DbgPrint("%s-TSEvtIoDeviceControl: "
                                "ixTimeSyncAccPTPPortConfigGet failed\n", 
						         DRIVERNAME);
				Status = STATUS_INVALID_PARAMETER;
			}

            BytesReturned = sizeof(IxTimeSyncAccPortCfgIoctl); 
            break;

        case IOCTL_RX_POLL:
        case IOCTL_TX_POLL:

            //
            // request to retreive either the rx or tx timestamp
            //

            // retreive user buffer to return timestamp 
            Status = WdfRequestRetrieveInputBuffer(Request, 
                                               sizeof(IxTimeSyncAccRxTxPollIoctl), 
                                                   &RequestBuffer, 
                                                   NULL);
	        if ( !NT_SUCCESS(Status) ) 
	        {
                DbgPrint("%s-TSEvtIoDeviceControl: WdfRequestRetrieveInputBuffer "
                                "failed\n", DRIVERNAME);
                break;
            }

            // cast user buffer to RxTxPoll struct
            pRxTxPollIoctl = (IxTimeSyncAccRxTxPollIoctl*)RequestBuffer; 

            // if requesting to retreive rx timestamp
            if ( IoControlCode == IOCTL_RX_POLL )
            {
                // retreive rx timestamp
			    if ( ixTimeSyncAccPTPRxPoll(pRxTxPollIoctl->ptpPort, 
                                          &pRxTxPollIoctl->ptpMsgData) 
					    != ICP_TIMESYNCACC_SUCCESS )
			    {
				    DbgPrint("%s-TSEvtIoDeviceControl: ixTimeSyncAccPTPRxPoll "
                                    "failed\n", 
						    DRIVERNAME);
				    Status = STATUS_INVALID_PARAMETER;
			    }
            }
            // if requesting to retreive tx timestamp
            else if ( IoControlCode == IOCTL_TX_POLL )
            {
                // retreive tx timestamp
			    if ( ixTimeSyncAccPTPTxPoll(pRxTxPollIoctl->ptpPort, 
                                          &pRxTxPollIoctl->ptpMsgData) 
					    != ICP_TIMESYNCACC_SUCCESS )
			    {
				    DbgPrint("%s-TSEvtIoDeviceControl: ixTimeSyncAccPTPTxPoll "
                                    "failed\n", 
						    DRIVERNAME);
				    Status = STATUS_INVALID_PARAMETER;
			    }
            }

            BytesReturned = sizeof(IxTimeSyncAccRxTxPollIoctl);
			break;

        case IOCTL_CAN_POLL:

            //
            // request to retreive a CAN port timestamp
            //

            // retreive user buffer to return timestamp 
            Status = WdfRequestRetrieveInputBuffer(Request, 
                                               sizeof(IxTimeSyncAccCANPollIoctl), 
                                                   &RequestBuffer, 
                                                   NULL);
	        if ( !NT_SUCCESS(Status) ) 
	        {
                DbgPrint("%s-TSEvtIoDeviceControl: WdfRequestRetrieveInputBuffer "
                                "failed\n", DRIVERNAME);
                break;
            }

            // cast user buffer to a CANPoll struct
            pCANPollIoctl = (IxTimeSyncAccCANPollIoctl*)RequestBuffer; 

            // retreive the CAN port timestamp
			if ( ixTimeSyncAccPTPCANPoll(pCANPollIoctl->ptpPort, 
                                       &pCANPollIoctl->ptpTimeStamp) 
				    != ICP_TIMESYNCACC_SUCCESS )
			{
				DbgPrint("%s-TSEvtIoDeviceControl: "
                                "ixTimeSyncAccPTPCANPoll failed\n", 
						         DRIVERNAME);
			    Status = STATUS_INVALID_PARAMETER;
			}

            BytesReturned = sizeof(IxTimeSyncAccCANPollIoctl); 
            break;

        case IOCTL_SYS_TIME_SET:
        case IOCTL_TARG_TIME_SET:
        case IOCTL_AUX_TARG_TIME_SET:

            //
            // request to set the system time or a 1588 timer
            //

            // retreive parameters from user buffer 
            Status = WdfRequestRetrieveInputBuffer(Request, 
                                                   sizeof(IxTimeSyncAccTimeValue), 
                                                   &RequestBuffer, 
                                                   NULL);
	        if ( !NT_SUCCESS(Status) ) 
	        {
                DbgPrint("%s-TSEvtIoDeviceControl: WdfRequestRetrieveInputBuffer "
                                "failed\n", DRIVERNAME);
                break;
            }

            // copy time value from user buffer
            memcpy(&TimeValue, RequestBuffer, sizeof(IxTimeSyncAccTimeValue));

            // set system time
            if ( IoControlCode == IOCTL_SYS_TIME_SET )
            {
			    if ( ixTimeSyncAccSystemTimeSet(TimeValue) 
				        != ICP_TIMESYNCACC_SUCCESS )
			    {
				    DbgPrint("%s-TSEvtIoDeviceControl: "
                                    "ixTimeSyncAccSystemTimeSet failed\n", 
						            DRIVERNAME);
			        Status = STATUS_INVALID_PARAMETER;
			    }
            }
            // set target time
            else if ( IoControlCode == IOCTL_TARG_TIME_SET )
            {
			    if ( ixTimeSyncAccTargetTimeSet(TimeValue) 
				        != ICP_TIMESYNCACC_SUCCESS )
			    {
				    DbgPrint("%s-TSEvtIoDeviceControl: "
                                    "ixTimeSyncAccTargetTimeSet failed\n", 
						            DRIVERNAME);
			        Status = STATUS_INVALID_PARAMETER;
			    }
            }
            // set aux target time
            else if ( IoControlCode == IOCTL_AUX_TARG_TIME_SET )
            {
			    if ( ixTimeSyncAccAuxTargetTimeSet(TimeValue) 
				        != ICP_TIMESYNCACC_SUCCESS )
			    {
				    DbgPrint("%s-TSEvtIoDeviceControl: "
                                    "ixTimeSyncAccAuxTargetTimeSet failed\n", 
						            DRIVERNAME);
			        Status = STATUS_INVALID_PARAMETER;
			    }
            }
            break;

        case IOCTL_SYS_TIME_GET:
        case IOCTL_TARG_TIME_GET:
        case IOCTL_AUX_TARG_TIME_GET:

			// 
			// request to retreive system/targ/aux targ time
			//

            // retreive user buffer to return time value 
            Status = WdfRequestRetrieveInputBuffer(Request, 
                                                   sizeof(IxTimeSyncAccTimeValue), 
                                                   &RequestBuffer, 
                                                   NULL);
	        if ( !NT_SUCCESS(Status) ) 
	        {
                DbgPrint("%s-TSEvtIoDeviceControl: WdfRequestRetrieveInputBuffer "
                                "failed\n", DRIVERNAME);
                break;
            }

            // get system time
            if ( IoControlCode == IOCTL_SYS_TIME_GET )
            {
			    if ( ixTimeSyncAccSystemTimeGet(&TimeValue) 
				        != ICP_TIMESYNCACC_SUCCESS )
			    {
				    DbgPrint("%s-TSEvtIoDeviceControl: "
                                    "ixTimeSyncAccSystemTimeGet failed\n", 
						            DRIVERNAME);
			        Status = STATUS_INVALID_PARAMETER;
                    break; 
			    }
            }
            // get target time
            else if ( IoControlCode == IOCTL_TARG_TIME_GET )
            {
			    if ( ixTimeSyncAccTargetTimeGet(&TimeValue) 
				        != ICP_TIMESYNCACC_SUCCESS )
			    {
				    DbgPrint("%s-TSEvtIoDeviceControl: "
                                    "ixTimeSyncAccTargetTimeGet failed\n", 
						            DRIVERNAME);
			        Status = STATUS_INVALID_PARAMETER;
                    break;
			    }
            }
            // get aux target time
            else if ( IoControlCode == IOCTL_AUX_TARG_TIME_GET )
            {
			    if ( ixTimeSyncAccAuxTargetTimeGet(&TimeValue) 
				        != ICP_TIMESYNCACC_SUCCESS )
			    {
				    DbgPrint("%s-TSEvtIoDeviceControl: "
                                    "ixTimeSyncAccAuxTargetTimeGet failed\n", 
						            DRIVERNAME);
			        Status = STATUS_INVALID_PARAMETER;
                    break;
			    }
            } else {
                break; /* impossible. Just to avoid klockwork complains */
            }

            // copy time value into user buffer
            memcpy(RequestBuffer, &TimeValue, sizeof(IxTimeSyncAccTimeValue));

            BytesReturned = sizeof(IxTimeSyncAccTimeValue);
            break;

        case IOCTL_TICK_RATE_GET:
        case IOCTL_TICK_RATE_SET:
        case IOCTL_PULSE_PER_SEC_TIME_SET:
        case IOCTL_PULSE_PER_SEC_TIME_GET:

			// 
			// request to get/set tick rate or pulse per second time
			//

            // retreive user buffer
            Status = WdfRequestRetrieveInputBuffer(Request, 
                                                   sizeof(UINT32), 
                                                   &RequestBuffer, 
                                                   NULL);
	        if ( !NT_SUCCESS(Status) ) 
	        {
                DbgPrint("%s-TSEvtIoDeviceControl: WdfRequestRetrieveInputBuffer "
                                "failed\n", DRIVERNAME);
                break;
            }
            
            // get tick rate
            if ( IoControlCode == IOCTL_TICK_RATE_GET )
            {
			    if ( ixTimeSyncAccTickRateGet(&val32) 
				        != ICP_TIMESYNCACC_SUCCESS )
			    {
				    DbgPrint("%s-TSEvtIoDeviceControl: "
                                    "ixTimeSyncAccTickRateGet failed\n", 
						            DRIVERNAME);
			        Status = STATUS_INVALID_PARAMETER;
			    }

                memcpy(RequestBuffer, &val32, sizeof(UINT32));

                BytesReturned = sizeof(UINT32);
            }
            // set tick rate
            else if ( IoControlCode == IOCTL_TICK_RATE_SET )
            {
                memcpy(&val32, (UINT32*)RequestBuffer, sizeof(UINT32)); 

			    if ( ixTimeSyncAccTickRateSet(val32) 
				        != ICP_TIMESYNCACC_SUCCESS )
			    {
				    DbgPrint("%s-TSEvtIoDeviceControl: "
                                    "ixTimeSyncAccTickRateSet failed\n", 
						            DRIVERNAME);
			        Status = STATUS_INVALID_PARAMETER;
			    }
            }
            // get pulse per second time
            else if ( IoControlCode == IOCTL_PULSE_PER_SEC_TIME_GET )
            {
			    if ( ixTimeSyncAccPulsePerSecondTimeGet(&val32) 
				        != ICP_TIMESYNCACC_SUCCESS )
			    {
				    DbgPrint("%s-TSEvtIoDeviceControl: "
                                    "ixTimeSyncAccPulsePerSecondTimeGet failed\n", 
						            DRIVERNAME);
			        Status = STATUS_INVALID_PARAMETER;
			    }

                memcpy(RequestBuffer, &val32, sizeof(UINT32));

                BytesReturned = sizeof(UINT32);
            }
            // set pulse per second
            else if ( IoControlCode == IOCTL_PULSE_PER_SEC_TIME_SET )
            {
                memcpy(&val32, (UINT32*)RequestBuffer, sizeof(UINT32)); 

			    if ( ixTimeSyncAccPulsePerSecondTimeSet(val32) 
				                        != ICP_TIMESYNCACC_SUCCESS )
			    {
				    DbgPrint("%s-TSEvtIoDeviceControl: "
                                    "ixTimeSyncAccPulsePerSecondTimeSet failed\n", 
						            DRIVERNAME);
			        Status = STATUS_INVALID_PARAMETER;
			    }
            }
            break;

        case IOCTL_TARG_TIME_POLL:
        case IOCTL_AUX_TARG_TIME_POLL:
        case IOCTL_AUX_TIME_POLL:

            //
            // request to poll the time status of the 1588 timers
            //

            // retreive the user buffer to return the time value
            Status = WdfRequestRetrieveInputBuffer(Request, 
                                               sizeof(IxTimeSyncAccTimePollIoctl), 
                                                   &RequestBuffer, 
                                                   NULL);
	        if ( !NT_SUCCESS(Status) ) 
	        {
                DbgPrint("%s-TSEvtIoDeviceControl: WdfRequestRetrieveInputBuffer "
                                "failed\n", DRIVERNAME);
                break;
            }

            // cast the user buff to a TimePollIoctl struct
            pTimePollIoctl = (IxTimeSyncAccTimePollIoctl*)RequestBuffer; 
               
            // poll the target time
            if ( IoControlCode == IOCTL_TARG_TIME_POLL )
            {
			    if ( ixTimeSyncAccTargetTimePoll(&pTimePollIoctl->PollFlag, 
                                               &pTimePollIoctl->timeVal) 
				        != ICP_TIMESYNCACC_SUCCESS )
			    {
				    DbgPrint("%s-TSEvtIoDeviceControl: "
                                    "ixTimeSyncAccTargetTimePoll failed\n", 
						            DRIVERNAME);
			        Status = STATUS_INVALID_PARAMETER;
			    }
            }
            // poll the aux target time
            else if ( IoControlCode == IOCTL_AUX_TARG_TIME_POLL )
            {
			    if ( ixTimeSyncAccAuxTargetTimePoll(&pTimePollIoctl->PollFlag, 
                                                  &pTimePollIoctl->timeVal) 
				        != ICP_TIMESYNCACC_SUCCESS )
			    {
				    DbgPrint("%s-TSEvtIoDeviceControl: "
                                    "ixTimeSyncAccAuxTargetTimePoll failed\n", 
						            DRIVERNAME);
			        Status = STATUS_INVALID_PARAMETER;
			    }
            }
            // poll the aux time
            else if ( IoControlCode == IOCTL_AUX_TIME_POLL )
            {
			    if ( ixTimeSyncAccAuxTimePoll(pTimePollIoctl->auxMode, 
                                            &pTimePollIoctl->PollFlag,
                                            &pTimePollIoctl->timeVal) 
				        != ICP_TIMESYNCACC_SUCCESS )
			    {
				    DbgPrint("%s-TSEvtIoDeviceControl: "
                                    "ixTimeSyncAccAuxTimePoll failed\n", 
						            DRIVERNAME);
			        Status = STATUS_INVALID_PARAMETER;
			    }
            }

            BytesReturned = sizeof(IxTimeSyncAccTimePollIoctl);
            break;

        case IOCTL_RESET:

            //
            // request a complete hardware reset
            //

            // reset the 1588 hardware
			if ( ixTimeSyncAccReset() != ICP_TIMESYNCACC_SUCCESS )
			{
				DbgPrint("%s-TSEvtIoDeviceControl: "
                                "ixTimeSyncAccReset failed\n", 
						         DRIVERNAME);
				Status = STATUS_INVALID_PARAMETER;
			}
			break;

        case IOCTL_CHNL_RESET:

            //
            // request to reset a single channel
            //

            // retreive the user buff
            Status = WdfRequestRetrieveInputBuffer(Request, 
                                               sizeof(IxTimeSyncAcc1588PTPPort), 
                                                   &RequestBuffer, 
                                                   NULL);
	        if ( !NT_SUCCESS(Status) ) 
	        {
                DbgPrint("%s-TSEvtIoDeviceControl: WdfRequestRetrieveInputBuffer "
                                    "failed\n", DRIVERNAME);
                break;
            }

            // copy the passed in parameter (port number to reset) 
            memcpy(&ptpPort, RequestBuffer, sizeof(IxTimeSyncAcc1588PTPPort));

            // reset the specified channel
			if ( ixTimeSyncAccChnlReset(ptpPort) != ICP_TIMESYNCACC_SUCCESS )
			{
				DbgPrint("%s-TSEvtIoDeviceControl: "
                                "ixTimeSyncAccChnlReset failed\n", 
						         DRIVERNAME);
				Status = STATUS_INVALID_PARAMETER;
			}
			break;

        case IOCTL_SHOW_ALL:

            //
            // request to display all 1588 register contents
            //

            // display regs
			if ( ixTimeSyncAccShow() != ICP_TIMESYNCACC_SUCCESS )
			{
				DbgPrint("%s-TSEvtIoDeviceControl: "
                                "ixTimeSyncAccShow failed\n", 
						         DRIVERNAME);
				Status = STATUS_INVALID_PARAMETER;
			}
			break;

        case IOCTL_STATS_GET:

            //
            // request to get 1588 statistics
            //

            Status = WdfRequestRetrieveInputBuffer(Request, 
                                                   sizeof(IxTimeSyncAccStats), 
                                                   &RequestBuffer, 
                                                   NULL);
	        if ( !NT_SUCCESS(Status) ) 
	        {
                DbgPrint("%s-TSEvtIoDeviceControl: WdfRequestRetrieveInputBuffer "
                                "failed\n", DRIVERNAME);
                break;
            }

            // get statistics
			if ( ixTimeSyncAccStatsGet((IxTimeSyncAccStats*)RequestBuffer) 
			        != ICP_TIMESYNCACC_SUCCESS )
			{
				DbgPrint("%s-TSEvtIoDeviceControl: "
                                "ixTimeSyncAccStatsGet failed\n", 
						         DRIVERNAME);
			    Status = STATUS_INVALID_PARAMETER;
			}

            BytesReturned = sizeof(IxTimeSyncAccStats); 
            break;
            
      case IOCTL_PORT_VERSION_SET:

            // retreive parameters from user buffer 
            Status = WdfRequestRetrieveInputBuffer(Request, 
                                                sizeof(IxTimeSyncAccVersionIoctl), 
                                                   &RequestBuffer, 
                                                   NULL);
	        if ( !NT_SUCCESS(Status) ) 
	        {
                DbgPrint("%s-IxTimeSyncAccVersionIoctl: WdfRequestRetrieveInputBuffer "
                                "failed\n", DRIVERNAME);
                break;
            }

            pVersionIoctl = (IxTimeSyncAccVersionIoctl*)RequestBuffer; 

            if ( ixTimeSyncAccPTPVersionSet(pVersionIoctl->ptpPort, 
                               pVersionIoctl->ptpVersion) 
                    != ICP_TIMESYNCACC_SUCCESS )
            {
                DbgPrint("%s-timesync_ioctl: \
                    ixTimeSyncAccPTPVersionSet failed\n", 
                        DRIVERNAME);
				Status = STATUS_INVALID_PARAMETER;
            }
            break;
    
      case IOCTL_PORT_VERSION_GET:
            // retreive parameters from user buffer 
            Status = WdfRequestRetrieveInputBuffer(Request, 
                                                sizeof(IxTimeSyncAccVersionIoctl), 
                                                   &RequestBuffer, 
                                                   NULL);
	        if ( !NT_SUCCESS(Status) ) 
	        {
                DbgPrint("%s-IxTimeSyncAccVersionIoctl: WdfRequestRetrieveInputBuffer "
                                "failed\n", DRIVERNAME);
                break;
            }

            pVersionIoctl = (IxTimeSyncAccVersionIoctl*)RequestBuffer; 

            if ( ixTimeSyncAccPTPVersionGet(pVersionIoctl->ptpPort, 
                               &pVersionIoctl->ptpVersion) 
                    != ICP_TIMESYNCACC_SUCCESS )
            {
                DbgPrint("%s-timesync_ioctl: \
                    ixTimeSyncAccPTPVersionGet failed\n", 
                        DRIVERNAME);
				Status = STATUS_INVALID_PARAMETER;
            }
            BytesReturned = sizeof(IxTimeSyncAccVersionIoctl); 
            break;

  
      case IOCTL_PORT_OPERATION_MODE_SET:
            // retreive parameters from user buffer 
            Status = WdfRequestRetrieveInputBuffer(Request, 
                                                sizeof(IxTimeSyncAccOperationModeIoctl), 
                                                   &RequestBuffer, 
                                                   NULL);
	        if ( !NT_SUCCESS(Status) ) 
	        {
                DbgPrint("%s-IxTimeSyncAccOperationModeIoctl: WdfRequestRetrieveInputBuffer "
                                "failed\n", DRIVERNAME);
                break;
            }

            pOperationModeIoctl = (IxTimeSyncAccOperationModeIoctl*)RequestBuffer; 

            if ( ixTimeSyncAccPTPOperationModeSet(pOperationModeIoctl->ptpPort, 
                               pOperationModeIoctl->ptpOpMode) 
                    != ICP_TIMESYNCACC_SUCCESS )
            {
                DbgPrint("%s-timesync_ioctl: \
                    ixTimeSyncAccPTPOperationModeSet failed\n", 
                        DRIVERNAME);
				Status = STATUS_INVALID_PARAMETER;
            }
            break;
    
      case IOCTL_PORT_OPERATION_MODE_GET:

            // retreive parameters from user buffer 
            Status = WdfRequestRetrieveInputBuffer(Request, 
                                                sizeof(IxTimeSyncAccOperationModeIoctl), 
                                                   &RequestBuffer, 
                                                   NULL);
	        if ( !NT_SUCCESS(Status) ) 
	        {
                DbgPrint("%s-IxTimeSyncAccOperationModeIoctl: WdfRequestRetrieveInputBuffer "
                                "failed\n", DRIVERNAME);
                break;
            }

            pOperationModeIoctl = (IxTimeSyncAccOperationModeIoctl*)RequestBuffer; 


            if ( ixTimeSyncAccPTPOperationModeGet(pOperationModeIoctl->ptpPort, 
                               &pOperationModeIoctl->ptpOpMode) 
                    != ICP_TIMESYNCACC_SUCCESS )
            {
                DbgPrint("%s-timesync_ioctl: \
                    ixTimeSyncAccPTPOperationModeGet failed\n", 
                        DRIVERNAME);
				Status = STATUS_INVALID_PARAMETER;
            }
            BytesReturned = sizeof(IxTimeSyncAccOperationModeIoctl); 
            break;


        case IOCTL_STATS_RESET:

            //
            // request to reset 1588 statistics
            //

            // reset stats
            ixTimeSyncAccStatsReset();
			break;

        default:
            DbgPrint("%s-TSEvtIoDeviceControl: unknown ioctl command\n", DRIVERNAME);
            Status = STATUS_INVALID_DEVICE_REQUEST;
    }

    // return ioctl status and data to user
    WdfRequestCompleteWithInformation(Request, Status, BytesReturned);
}


void
TargTimeCallback(
	IxTimeSyncAccTimeValue targetTime
	)
/*++

Routine Description:

    This is the target time callback routine. It is called from 
    the ISR in the HAL when a target time interrupt is generated.  
    The notify event is signaled to indicate that the target 
    time has expired. 

Arguments:

    targetTime - target time register timestamp 


Return Value:

    none

--*/
{
    // copy the target time value to the global value to be read by the 
    // notify ioctl
    memcpy(&gTargTimeIoctl, &targetTime, sizeof(IxTimeSyncAccTimeValue));

    // signal the notify ioctl that the target time has expired
    KeSetEvent((PRKEVENT)&gNotifyEvent[TARG_TIME_EVENT_NUM], IO_NO_INCREMENT, 
                FALSE);
}


void
AuxTimeCallback(IxTimeSyncAccAuxMode auxMode,
             IxTimeSyncAccTimeValue auxTime
             )
/*++

Routine Description:

    This is the aux time callback routine. It is called from 
    the ISR in the HAL when an aux time interrupt is generated.  
    The notify event is signaled to indicate that an aux time 
    snapshot has occured. 

Arguments:

    auxMode - master, slave, or any 
    auxTime - aux time register timestamp

Return Value:

    none

--*/
{
    // copy the aux time value and aux mode to the global value 
    // to be read by the notify ioctl
    gAuxTimeIoctl.auxMode = auxMode;
    memcpy(&gAuxTimeIoctl.auxTime, &auxTime, sizeof(IxTimeSyncAccAuxTimeIoctl));

    // signal the notify ioctl that the aux timestamp has been set
    KeSetEvent((PRKEVENT)&gNotifyEvent[AUX_TIME_EVENT_NUM], IO_NO_INCREMENT, 
                FALSE);
}


void 
AuxTargetTimeCallback(
            IxTimeSyncAccTimeValue auxTargetTime
            )
/*++

Routine Description:

    This is the aux target time callback routine. It is called from 
    the ISR in the HAL when the aux target time interrupt is generated.  
    The notify event is signaled to indicate that the aux target 
    time has expired. 

Arguments:

    auxTargetTime - aux target time register timestamp 


Return Value:

    none

--*/
{
    // copy the target time value to the global value to be read by the 
    // notify ioctl
    memcpy(&gAuxTargTimeIoctl, &auxTargetTime, sizeof(IxTimeSyncAccTimeValue));

    // signal the notify ioctl that the aux target time has expired
    KeSetEvent((PRKEVENT)&gNotifyEvent[AUX_TARG_TIME_EVENT_NUM], IO_NO_INCREMENT,
                FALSE);
}


void 
PulsePerSecondCallback(
             UINT32 pulsePerSecond
             )
/*++

Routine Description:

    This is the pulse per second callback routine. It is called from 
    the ISR in th*e HAL when the pps time interrupt is generated.  
    The notify event is signaled to indicate that the pps 
    time has expired. 

Arguments:

    pulsePerSecond - pulse per second register timestamp 


Return Value:

    none

--*/
{
    // copy the pulse per second time value to the global value 
    // to be read by the notify ioctl
    gPulsePerSecondIoctl = pulsePerSecond; 

    // signal the notify ioctl that the pulse per second time has expired
    KeSetEvent((PRKEVENT)&gNotifyEvent[PPS_EVENT_NUM], IO_NO_INCREMENT, FALSE);
}


/*++

Routine Description:

    SaveRegState is called to save device registers before being put into a 
    low power state.

Arguments:

    none 
        
Return Value:

    none

--*/
void SaveRegState(WDFDEVICE Device)
{
	timesync_data_t *ts_data = WdfObjectGet_timesync_data_t(Device);

    ts_data->state.AmmsInterrupt = ixTimeSyncAccControlAmmsInterruptMaskGet();
	ts_data->state.AsmsInterrupt = ixTimeSyncAccControlAsmsInterruptMaskGet();
	ts_data->state.TtmInterrupt = ixTimeSyncAccControlTtmInterruptMaskGet();
	ts_data->state.AtmInterrupt = ixTimeSyncAccControlAtmInterruptMaskGet();
	ts_data->state.PpsmInterrupt = ixTimeSyncAccControlPpsmInterruptMaskGet();
	ixTimeSyncAccSystemTimeSnapshotGet(&ts_data->state.SystemTime.timeValueLowWord, 
                                       &ts_data->state.SystemTime.timeValueHighWord);
	ixTimeSyncAccTargetTimeSnapshotGet(&ts_data->state.TargetTime.timeValueLowWord, 
                                       &ts_data->state.TargetTime.timeValueHighWord);
	ixTimeSyncAccAuxTargetTimeSnapshotGet(&ts_data->state.AuxTargetTime.timeValueLowWord, 
                                          &ts_data->state.AuxTargetTime.timeValueHighWord);
	ixTimeSyncAccPulsePerSecondCompareGet(&ts_data->state.PulsePerSecond);
	ixTimeSyncAccAddendFsvGet(&ts_data->state.Addend);

	ts_data->state.MasterMode[ICP_TIMESYNCACC_GBE_0_1588PTP_PORT] = 
        ixTimeSyncAccControlPTPPortMasterModeGet(ICP_TIMESYNCACC_GBE_0_1588PTP_PORT);
	ts_data->state.MasterMode[ICP_TIMESYNCACC_GBE_1_1588PTP_PORT] = 
        ixTimeSyncAccControlPTPPortMasterModeGet(ICP_TIMESYNCACC_GBE_1_1588PTP_PORT);
	
    ts_data->state.MsgTimestamp[ICP_TIMESYNCACC_GBE_0_1588PTP_PORT] = 
        ixTimeSyncAccControlPTPPortPTPMsgTimestampGet(ICP_TIMESYNCACC_GBE_0_1588PTP_PORT);
    ts_data->state.MsgTimestamp[ICP_TIMESYNCACC_GBE_1_1588PTP_PORT] = 
        ixTimeSyncAccControlPTPPortPTPMsgTimestampGet(ICP_TIMESYNCACC_GBE_1_1588PTP_PORT);
}


/*++

Routine Description:

    RestoreRegState is called to restore device registers when being put 
    into a full power state.

Arguments:

    none 
        
Return Value:

    none

--*/
void RestoreRegState(WDFDEVICE Device)
{
	timesync_data_t *ts_data = WdfObjectGet_timesync_data_t(Device);

	//!!!ixTimeSyncAccSystemTimeSnapshotSet(ts_data->state.SystemTime.timeValueLowWord, 
    //                                   ts_data->state.SystemTime.timeValueHighWord);
	ixTimeSyncAccTargetTimeSnapshotSet(ts_data->state.TargetTime.timeValueLowWord, 
                                       ts_data->state.TargetTime.timeValueHighWord);
	ixTimeSyncAccAuxTargetTimeSnapshotSet(ts_data->state.AuxTargetTime.timeValueLowWord, 
                                          ts_data->state.AuxTargetTime.timeValueHighWord);
	ixTimeSyncAccPulsePerSecondCompareSet(ts_data->state.PulsePerSecond);
	ixTimeSyncAccAddendFsvSet(ts_data->state.Addend);

	ixTimeSyncAccControlPTPPortMasterModeSet(ICP_TIMESYNCACC_GBE_0_1588PTP_PORT, 
                             ts_data->state.MasterMode[ICP_TIMESYNCACC_GBE_0_1588PTP_PORT]);
	ixTimeSyncAccControlPTPPortMasterModeSet(ICP_TIMESYNCACC_GBE_1_1588PTP_PORT,
                             ts_data->state.MasterMode[ICP_TIMESYNCACC_GBE_1_1588PTP_PORT]);
	
    ixTimeSyncAccControlPTPPortPTPMsgTimestampSet(ICP_TIMESYNCACC_GBE_0_1588PTP_PORT,
                             ts_data->state.MsgTimestamp[ICP_TIMESYNCACC_GBE_0_1588PTP_PORT]);
    ixTimeSyncAccControlPTPPortPTPMsgTimestampSet(ICP_TIMESYNCACC_GBE_1_1588PTP_PORT,
                             ts_data->state.MsgTimestamp[ICP_TIMESYNCACC_GBE_1_1588PTP_PORT]);
}


/*++

Routine Description:

    DisableInterrupts disables all interrupts on the 1588 device.

Arguments:

    none 
        
Return Value:

    none

--*/
void DisableInterrupts()
{
	// make sure all interrupts are disabled
    ixTimeSyncAccControlAmmsInterruptMaskClear();
    ixTimeSyncAccControlAsmsInterruptMaskClear();
    ixTimeSyncAccControlTtmInterruptMaskClear();
    ixTimeSyncAccControlAtmInterruptMaskClear();
    ixTimeSyncAccControlPpsmInterruptMaskClear();
}


/*++

Routine Description:

    EnableInterrupts enables interrupts which were previously disabled 
    on the 1588 device.  

Arguments:

    none 
        
Return Value:

    none

--*/
void RestoreInterrupts(WDFDEVICE Device)
{
	timesync_data_t *ts_data = WdfObjectGet_timesync_data_t(Device);

    if ( ts_data->state.AmmsInterrupt )
    {
        ixTimeSyncAccControlAmmsInterruptMaskSet();
    }

    if ( ts_data->state.AsmsInterrupt )
    {
        ixTimeSyncAccControlAsmsInterruptMaskSet();
    }

    if ( ts_data->state.TtmInterrupt )
    {
        ixTimeSyncAccControlTtmInterruptMaskSet();
    }

    if ( ts_data->state.AtmInterrupt )
    {
        ixTimeSyncAccControlAtmInterruptMaskSet();
    }

    if ( ts_data->state.PpsmInterrupt )
    {
        ixTimeSyncAccControlPpsmInterruptMaskSet();
    }
}


/*****************************************************************************
 * Direct R/W from config space.
 *****************************************************************************/
NTSTATUS
PCIConfigReadWrite(
    IN PDEVICE_OBJECT DeviceObject,
    IN ULONG          ReadWriteMode, 
    IN PVOID          Buffer,
    IN ULONG          Offset,
    IN ULONG          Length
    )
 {
	NTSTATUS status = STATUS_UNSUCCESSFUL;
    KEVENT pci_rw_event;
    PIRP pirp;
    IO_STATUS_BLOCK IoStatusBlock;
    PIO_STACK_LOCATION irpStack;
    PDEVICE_OBJECT targetObject;

    PAGED_CODE();

    KeInitializeEvent(&pci_rw_event, NotificationEvent, FALSE);
    targetObject = IoGetAttachedDeviceReference(DeviceObject);
    
	pirp = IoBuildSynchronousFsdRequest(
		IRP_MJ_PNP,
        targetObject,
        NULL,
        0,
        NULL,
        &pci_rw_event,
        &IoStatusBlock );

    if (pirp==NULL) {
        status = STATUS_INSUFFICIENT_RESOURCES;
        goto DONE;
    }

    irpStack = IoGetNextIrpStackLocation( pirp );
	if (ReadWriteMode==0) {
        irpStack->MinorFunction = IRP_MN_READ_CONFIG;
    }else {
        irpStack->MinorFunction = IRP_MN_WRITE_CONFIG;
    }

    irpStack->Parameters.ReadWriteConfig.WhichSpace = PCI_WHICHSPACE_CONFIG;
    irpStack->Parameters.ReadWriteConfig.Buffer = Buffer;
    irpStack->Parameters.ReadWriteConfig.Offset = Offset;
    irpStack->Parameters.ReadWriteConfig.Length = Length;

    pirp->IoStatus.Status = STATUS_NOT_SUPPORTED ;
    status = IoCallDriver(targetObject, pirp);
 
	if (status == STATUS_PENDING) {
        KeWaitForSingleObject(&pci_rw_event, Executive, KernelMode, FALSE, NULL);
        status = IoStatusBlock.Status;
    }

DONE:
    ObDereferenceObject(targetObject);

    return status;
} 


