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

/*****************************************************************************
 * @ingroup GPIO_GENERAL
 *
 * @file gpio.c
 *
 * @description
 *    The gpio module is a windows driver which provides a reference   
 *    implementation for the control of the gpio signals. 
 *    Communication is done through the DeviceIoControl API
 *    and gpio signal control is accomplished using the set of ioctl
 *    control codes which are defined for this driver.
 *
 *
 *****************************************************************************/


#include "gpio.h"


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

#ifdef ALLOC_PRAGMA
#pragma alloc_text( INIT, DriverEntry )
#pragma alloc_text(PAGE, GPIOEvtDeviceAdd)
#pragma alloc_text(PAGE, GPIOEvtDevicePrepareHardware)
#pragma alloc_text(PAGE, GPIOEvtDeviceReleaseHardware)
#endif

/* global pointer for GPIO registers */
gpio_regs_t gGPIORegs;


/*

Routine Description:

    Installable driver initialization entry point.
    This entry point is called directly by the I/O system.

Arguments:

    DriverObject - Pointer to the driver object created by the I/O manager.

    RegistryPath - Pointer to the driver specific key
                   \Registry
                      \Machine
                         \System
                            \CurrentControlSet
                               \Services
                                  \<DriverName>

Return Value:

    NTSTATUS

*/
NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
    )
{
    NTSTATUS status = STATUS_SUCCESS;
    WDF_DRIVER_CONFIG config;

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

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

/*

Routine Description:

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

Arguments:

    Driver - Handle to a framework driver object created in DriverEntry

    DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure.

Return Value:

    NTSTATUS

*/
NTSTATUS
GPIOEvtDeviceAdd(
    IN WDFDRIVER Driver,
    IN PWDFDEVICE_INIT DeviceInit
    )
{
    NTSTATUS status = STATUS_SUCCESS;
    WDFDEVICE hDevice;
    WDF_IO_QUEUE_CONFIG queueConfig;
    WDF_OBJECT_ATTRIBUTES objAttributes;
    WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
    WDF_INTERRUPT_CONFIG interruptConfig;

    PAGED_CODE();

    /* Zero out the PnpPowerCallbacks structure. */

    WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);

    /* Set Callbacks for any of the functions we are interested in.
       If no callback is set, Framework will take the default action
       by itself.

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


    /* 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, 
        DEVICE_EXTENSION
        );

    /* Create a framework device object. */

    status = WdfDeviceCreate(
        &DeviceInit,
        &objAttributes,
        &hDevice
        );

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

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

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



    /* create the ioctl queue
 
       Configure our default queue to handle
       incoming read and write requests. This queue is
       power managed by the framework, which is the default.
       This means requests will not be dispatched from this
       queue while the device is not in D0. */
    
    
    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 function to call */

    queueConfig.EvtIoDeviceControl = GPIOEvtIoDeviceControl;

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

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

    return status;
}

/*

Routine Description

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

Arguments:

    Device - Handle to a framework device object.

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

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

Return Value:

    WDF status code.
*/
NTSTATUS
  GPIOEvtDevicePrepareHardware(
    IN WDFDEVICE  Device,
    IN WDFCMRESLIST  ResourcesRaw,
    IN WDFCMRESLIST  ResourcesTranslated
    )
{
    NTSTATUS status = STATUS_SUCCESS;

    PAGED_CODE();

    /* get gpio hardware info */
    
    status = GetHardwareInfo();

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

    return status;
}

/*

Routine Description:

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

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

Arguments:

    Device - Handle to a framework device object.

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

Return Value:

    NTSTATUS
*/
NTSTATUS
  GPIOEvtDeviceReleaseHardware(
    IN WDFDEVICE  Device,
    IN WDFCMRESLIST  ResourcesTranslated
    )
{
    NTSTATUS status = STATUS_SUCCESS;

    PAGED_CODE();

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

    return status;
}


/*
Routine Description:

    This event is called when the framework receives IRP_MJ_DEVICE_CONTROL
    requests from the system.

Arguments:

    Queue - Handle to the framework queue object that is associated
            with the I/O request.
    Request - Handle to a framework 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
GPIOEvtIoDeviceControl(
    IN WDFQUEUE Queue,
    IN WDFREQUEST Request,
    IN size_t OutputBufferLength,
    IN size_t InputBufferLength,
    IN ULONG IoControlCode
    )
{
    NTSTATUS Status = STATUS_SUCCESS;
    WDFMEMORY Memory;
    gpio_ioctl_t *pInfo;
    ULONG Reg;
    size_t BufLength;
    ULONG val;
    ULONG SignalReg = 0;

    PAGED_CODE();

    /* the gpio_ioctl_t struct must be passed in and back out */

    if ( InputBufferLength < sizeof(gpio_ioctl_t) )
    {
        DbgPrint("%s-GPIOEvtIoDeviceControl: input buffer size is \
            invalid (%d)\n", DRIVERNAME ,InputBufferLength);
        Status = STATUS_INVALID_PARAMETER;
        WdfRequestComplete(Request, Status);
        return;
    }

    if ( OutputBufferLength < sizeof(gpio_ioctl_t) )
    {
        DbgPrint("%s-GPIOEvtIoDeviceControl: output buffer size is \
            invalid (%d)\n", DRIVERNAME, OutputBufferLength);
        Status = STATUS_INVALID_PARAMETER;
        WdfRequestComplete(Request, Status);
        return;
    }

     /* Since all the IOCTLs handled here are buffered, 
        WdfRequestRetrieveOutputBuffer &  WdfRequestRetrieveInputBuffer 
        return the same buffer pointer. */
    
    Status = WdfRequestRetrieveInputBuffer(Request, sizeof(gpio_ioctl_t), 
                            (PVOID)&pInfo, &BufLength);

    if ( !NT_SUCCESS(Status) ) 
    {
        DbgPrint("%s-GPIOEvtIoDeviceControl: \
                WdfRequestRetrieveInputBuffer failed\n", DRIVERNAME);
        WdfRequestComplete(Request, Status);
        return;
    }

    /* a register is only 32 bits */
    
    if ( pInfo->signal )
    {
        SignalReg = pInfo->signal % GPIO_REG_BITS;
    }

    /* Determine which I/O control code was specified. */

    switch ( IoControlCode )
    {
        case IOCTL_GET_PIN_INFO:
            /* make sure passed in signal is within max */
            if ( pInfo->signal >= MAX_GPIO_SIGNALS )
            {
                DbgPrint("%s-GPIOEvtIoDeviceControl: signal value is out of \
                    bounds (%d)\n", DRIVERNAME, pInfo->signal);
                Status = STATUS_INVALID_PARAMETER;
                goto Exit;
            }
            
            /*get the info for this gpio signal */
            GetPinInfo(pInfo->signal, pInfo->buff);
            break;

        case IOCTL_SET_TO_ALTERNATIVE:
        case IOCTL_SET_TO_GPIO:
            /* make sure passed in signal is within max */
            if ( pInfo->signal >= MAX_GPIO_SIGNALS )
            {
                DbgPrint("%s-GPIOEvtIoDeviceControl: signal value is out of \
                    bounds (%d)\n", DRIVERNAME, pInfo->signal);
                Status = STATUS_INVALID_PARAMETER;
                goto Exit;
            }

            /* set register to use */
            pInfo->signal < (MAX_GPIO_SIGNALS/SEL) ? 
                    (Reg = gGPIORegs.gpio_use_sel) : 
                    (Reg = gGPIORegs.gpio_use_sel2);

            /* read the register first */
            val = READ_PORT_ULONG((PULONG)Reg);

            if ( IoControlCode == IOCTL_SET_TO_ALTERNATIVE )
            {
                /* clear bit to use pin for native function */
                ClearBit(SignalReg, &val);
            }
            else
            {
                /* set bit to use pin for gpio */
                SetBit(SignalReg, &val);
            }

            /* write the data back with modified bit */
            WRITE_PORT_ULONG((PULONG)Reg, val);
            break;

        case IOCTL_SET_AS_INPUT:
        case IOCTL_SET_AS_OUTPUT:
            /* make sure passed in signal is within max */ 
            if ( pInfo->signal >= MAX_GPIO_SIGNALS )
            {
                DbgPrint("%s-GPIOEvtIoDeviceControl: signal value is out of \
                        bounds (%d)\n", DRIVERNAME, pInfo->signal);
                Status = STATUS_INVALID_PARAMETER;
                goto Exit;
            }

            /* set register to use */
            pInfo->signal < (MAX_GPIO_SIGNALS/SEL) ? 
                (Reg = gGPIORegs.gp_io_sel) : 
                (Reg = gGPIORegs.gp_io_sel2);

            /* read the register first  */
            val = READ_PORT_ULONG((PULONG)Reg);

            if ( IoControlCode == IOCTL_SET_AS_OUTPUT )
            {
                /* clear bit to use pin as output */
                ClearBit(SignalReg, &val);
            }
            else
            {
                /* set bit to use pin as input  */
                SetBit(SignalReg, &val);
            }

            /* write the data back with modified bit  */ 
            WRITE_PORT_ULONG((PULONG)Reg, val);
            
            break;

        case IOCTL_SET_HIGH: 
        case IOCTL_SET_LOW:
            /* make sure passed in signal is within max */
            if ( pInfo->signal >= MAX_GPIO_SIGNALS )
            {
                DbgPrint("%s-GPIOEvtIoDeviceControl: signal value is out of \
                        bounds (%d)\n", DRIVERNAME, pInfo->signal);
                Status = STATUS_INVALID_PARAMETER;
                goto Exit;
            }

            /* set register to use */
            pInfo->signal < (MAX_GPIO_SIGNALS/LVL) ? 
                (Reg = gGPIORegs.gp_lvl) : 
                (Reg = gGPIORegs.gp_lvl2);

            /* read the register first  */
            val = READ_PORT_ULONG((PULONG)Reg);

            if ( IoControlCode == IOCTL_SET_LOW )
            {
                /* write a 0 to pin */
                ClearBit(SignalReg, &val);
            }
            else
            {
                /* write a 1 to pin */
                SetBit(SignalReg, &val);
            }

            /* write the data back with modified bit */
            WRITE_PORT_ULONG((PULONG)Reg, val);

            break;

        case IOCTL_START_BLINK: 
        case IOCTL_STOP_BLINK:
            /* blink is valid for first 32 signals only */
            if ( pInfo->signal >= (MAX_GPIO_SIGNALS/FIRST_HALF) )
            {
                DbgPrint("%s-GPIOEvtIoDeviceControl: signal value is out of \
                        bounds (%d)\n", DRIVERNAME, pInfo->signal);
                Status = STATUS_INVALID_PARAMETER;
                goto Exit;
            }

            /* read the register first */
            val = READ_PORT_ULONG((PULONG)gGPIORegs.gpo_blink);

            if ( IoControlCode == IOCTL_STOP_BLINK )
            {
                /* set pin to non-blink mode  */
                ClearBit(SignalReg, &val);
            }
            else
            {
                /* set pin to blink mode */
                SetBit(SignalReg, &val);
            }

            /* write the data back with modified bit */ 
            WRITE_PORT_ULONG((PULONG)gGPIORegs.gpo_blink, val);
            
            break;

        case IOCTL_INVERTED_INPUT: 
        case IOCTL_NONINVERTED_INPUT:
            /* invert is valid for first 32 signals only */
            if ( pInfo->signal >= (MAX_GPIO_SIGNALS/FIRST_HALF) )
            {
                DbgPrint("%s-GPIOEvtIoDeviceControl: signal value is out of \
                    bounds (%d)", DRIVERNAME, pInfo->signal);
                Status = STATUS_INVALID_PARAMETER;
                goto Exit;
            }

            /* read the register first */
            val = READ_PORT_ULONG((PULONG)gGPIORegs.gpi_inv);

            if ( IoControlCode == IOCTL_NONINVERTED_INPUT )
            {
                /* set pin to noninverted mode */
                ClearBit(SignalReg, &val);
            }
            else
            {
                /* set pin to inverted mode */
                SetBit(SignalReg, &val);
            }

            /* write the data back with modified bit */ 
            WRITE_PORT_ULONG((PULONG)gGPIORegs.gpi_inv, val);
            
            break;

        case IOCTL_READ_LEVEL:
            /* make sure passed in signal is within max */ 
            if ( pInfo->signal >= MAX_GPIO_SIGNALS )
            {
                DbgPrint("%s-GPIOEvtIoDeviceControl: signal value is out of \
                        bounds (%d)\n", DRIVERNAME, pInfo->signal);
                Status = STATUS_INVALID_PARAMETER;
                goto Exit;
            }

            /* set register to use */
            pInfo->signal < (MAX_GPIO_SIGNALS/LVL) ? 
                (Reg = gGPIORegs.gp_lvl) : 
                (Reg = gGPIORegs.gp_lvl2);

            /* read the register */
            val = READ_PORT_ULONG((PULONG)Reg);

            /* return level of signal */
            pInfo->data = TestBit(SignalReg, val);
            
            break;

        case IOCTL_READ_GPIO_USE_SEL_DWORD:
            pInfo->data = READ_PORT_ULONG((PULONG)gGPIORegs.gpio_use_sel);
            break;

        case IOCTL_READ_GP_IO_SEL_DWORD:
            pInfo->data = READ_PORT_ULONG((PULONG)gGPIORegs.gp_io_sel);
            break;

        case IOCTL_READ_GP_LVL_DWORD:
            pInfo->data = READ_PORT_ULONG((PULONG)gGPIORegs.gp_lvl);
            break;

        case IOCTL_READ_GPO_BLINK_DWORD:                
            pInfo->data = READ_PORT_ULONG((PULONG)gGPIORegs.gpo_blink);
            break;

        case IOCTL_READ_GPI_INV_DWORD:
            pInfo->data = READ_PORT_ULONG((PULONG)gGPIORegs.gpi_inv);
            break;

        case IOCTL_READ_GPIO_USE_SEL2_DWORD:
            pInfo->data = READ_PORT_ULONG((PULONG)gGPIORegs.gpio_use_sel2);
            break;

        case IOCTL_READ_GP_IO_SEL2_DWORD:
            pInfo->data = READ_PORT_ULONG((PULONG)gGPIORegs.gp_io_sel2);
            break;

        case IOCTL_READ_GP_LVL2_DWORD:
            pInfo->data = READ_PORT_ULONG((PULONG)gGPIORegs.gp_lvl2);
            break;

        case IOCTL_WRITE_GPIO_USE_SEL_DWORD:
            WRITE_PORT_ULONG((PULONG)gGPIORegs.gpio_use_sel, pInfo->data);
            break;

        case IOCTL_WRITE_GP_IO_SEL_DWORD:
            WRITE_PORT_ULONG((PULONG)gGPIORegs.gp_io_sel, pInfo->data);
            break;

        case IOCTL_WRITE_GP_LVL_DWORD:
            WRITE_PORT_ULONG((PULONG)gGPIORegs.gp_lvl, pInfo->data);
            break;

        case IOCTL_WRITE_GPO_BLINK_DWORD:                
            WRITE_PORT_ULONG((PULONG)gGPIORegs.gpo_blink, pInfo->data);
            break;

        case IOCTL_WRITE_GPI_INV_DWORD:
            WRITE_PORT_ULONG((PULONG)gGPIORegs.gpi_inv, pInfo->data);
            break;

        case IOCTL_WRITE_GPIO_USE_SEL2_DWORD:
            WRITE_PORT_ULONG((PULONG)gGPIORegs.gpio_use_sel2, pInfo->data);
            break;

        case IOCTL_WRITE_GP_IO_SEL2_DWORD:
            WRITE_PORT_ULONG((PULONG)gGPIORegs.gp_io_sel2, pInfo->data);
            break;

        case IOCTL_WRITE_GP_LVL2_DWORD:
            WRITE_PORT_ULONG((PULONG)gGPIORegs.gp_lvl2, pInfo->data);
            break;

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

Exit:

    WdfRequestCompleteWithInformation(Request, Status, sizeof(gpio_ioctl_t));
}


/*
Routine Description:

    This function finds the LPC hardware device and reads the gpio 
    BAR. The gpio register ports are then set in the 
    GPIO_Regs_t structure.

Arguments:

    None

Return Value:

    NTSTATUS

*/
NTSTATUS 
GetHardwareInfo()
{
    NTSTATUS status = STATUS_SUCCESS;
    ULONG PCIAddress;
    ULONG physAddr;

    /* Create the pci address to access the config space on the 
       LPC (low pin count) device. The GPIO BAR is located in the extended
       config area of the LPC device. */
    PCIAddress = 0x80000000 | (LPC_BUS_NUM<<LPC_BUS_OFFSET) | 
                (LPC_DEVICE_NUM<<LPC_DEVICE_OFFSET) | 
                (LPC_FUNCTION_NUM<<LPC_FUNCTION_OFFSET);

    /* add the offset required in the config space to access the gpio BAR */
    PCIAddress += GPIO_BAR_OFFSET;

    /* get the gpio bar */
    ReadPCIConfig(PCIAddress, &physAddr);

    /* remove the lsb indicating io memory  */
    physAddr -= 1;

    /* set the addrs in the i/o memory addr structure */
    set_reg_addrs(physAddr);

    return STATUS_SUCCESS;
}


/*
Routine Description:

    Set i/o memory pointers.

Arguments:

    addr    base port address of gpio registers

Return Value:

    VOID
*/
VOID set_reg_addrs(IN ULONG addr)
{
    gGPIORegs.gpio_use_sel = addr + GPIO_USE_SEL; 
    gGPIORegs.gp_io_sel = addr + GP_IO_SEL; 
    gGPIORegs.gp_lvl = addr + GP_LVL; 
    gGPIORegs.gpo_blink = addr + GPO_BLINK; 
    gGPIORegs.gpi_inv = addr + GPI_INV; 
    gGPIORegs.gpio_use_sel2 = addr + GPIO_USE_SEL2; 
    gGPIORegs.gp_io_sel2 = addr + GP_IO_SEL2; 
    gGPIORegs.gp_lvl2 = addr + GP_LVL2; 
}


/*
Routine Description:

    This function retreives a dword of data from pci configuration space

Arguments:

    pci_offset  pci address + offset into config space
    read_data   return data buffer 

Return Value:

    VOID
*/

VOID ReadPCIConfig(ULONG pci_offset, ULONG *read_data)
{
    ULONG tmp;

    __asm 
    {
        mov dx, 0xCF8/*PCI_CONFIG_ADDR_REG */ 
        mov eax, pci_offset
        out dx, eax 
        mov dx, 0xCFC/*PCI_CONFIG_DATA_REG */
        in eax, dx 
        mov tmp, eax 
    }
    *read_data=tmp;
}

/*
Routine Description:

    This function will set the indicated bit.

Arguments:

    bit    bit number to set
    pData  data value to set bit in 

Return Value:

    VOID
*/
VOID
SetBit(IN ULONG bit, IN PULONG pData)
{
    /* set indicated bit in register data */
    ULONG bitMask = 1 << bit;
    *pData |= bitMask;
}

/*
Routine Description:

    This function will clear the indicated bit.

Arguments:

    bit    bit number to clear
    pData  data value to clear bit in 

Return Value:

    VOID
*/
VOID
ClearBit(IN ULONG bit, IN PULONG pData)
{
    /* clear indicated bit in data */
    ULONG bitMask = 1 << bit;
    bitMask = ~bitMask;
    *pData &= bitMask;
}

/*
Routine Description:

    This function will set the indicated bit.

Arguments:

    bit    bit number to test
    Data  data value to test bit in 

Return Value:

    0 (clear) or 1 (set)
*/
ULONG
TestBit(IN ULONG bit, IN ULONG Data)
{
    ULONG bitMask;

    /* set bitmask to test bit */
    bitMask = 1 << bit;
    
    /* if bit is set */
    if ( Data & bitMask )
    {
        /* bit is set */
        return 1;
    }

    /* bit is clear  */
    return 0;
}


/*
Routine Description:

    This function will consruct a string which describes the current
    settings of the gpio signal. The string format is the following:
     
        Signal # - Use setting: GPIO or alt, IO setting: input or output, 
                   Level: low or high, Blink: on or off, Inversion: on or off

Arguments:

    bit    bit number to set
    pData  data value to set bit in 

Return Value:

    0 - success
    < 0 - error
*/
LONG GetPinInfo(ULONG signal, UCHAR *pBuff)
{
    ULONG Reg;
    ULONG data;

    /* make sure passed in signal is within max  */ 
    if ( signal >= MAX_GPIO_SIGNALS )
    {
        KdPrint(("%s-GetPinInfo: signal value is out of bounds (%d)\n", \
            DRIVERNAME, signal));
        return -1;
    }

    sprintf(pBuff, "Signal# %d - ", signal);


    /* get current use setting: native or gpio */

    strcat(pBuff, "Use setting:");

    /* obtain the correct register pointer */
    signal < (MAX_GPIO_SIGNALS/SEL) ? 
            (Reg = gGPIORegs.gpio_use_sel) : 
            (Reg = gGPIORegs.gpio_use_sel2);

    /* get current data from register */
    data = READ_PORT_ULONG((PULONG)Reg);

    /* if set as gpio  */
    if ( TestBit(signal, data) )
    {
        strcat(pBuff, "gpio");
    }
    else
    {
        strcat(pBuff, "alt");
    }

    strcat(pBuff, ", ");


    /* get current io setting: input or output */

    strcat(pBuff, "IO setting:");

    signal < (MAX_GPIO_SIGNALS/SEL) ? 
            (Reg = gGPIORegs.gp_io_sel) : 
            (Reg = gGPIORegs.gp_io_sel2);

    /* get current data from register  */
    data = READ_PORT_ULONG((PULONG)Reg);

    /* if set as input  */
    if ( TestBit(signal, data) )
    {
        strcat(pBuff, "input");
    }
    else
    {
        strcat(pBuff, "output");
    }

    strcat(pBuff, ", ");


    /* get current pin level: high or low */

    strcat(pBuff, "Level:");

    signal < (MAX_GPIO_SIGNALS/LVL) ? 
            (Reg = gGPIORegs.gp_lvl) : 
            (Reg = gGPIORegs.gp_lvl2);

    /* get current data from register  */
    data = READ_PORT_ULONG((PULONG)Reg);

    /* if high */
    if ( TestBit(signal, data) )
    {
        strcat(pBuff, "high");
    }
    else
    {
        strcat(pBuff, "low");
    }

    strcat(pBuff, ", ");


    /* get blink setting  */

    strcat(pBuff, "Blink:");

    /* get current data from register */
    data = READ_PORT_ULONG((PULONG)gGPIORegs.gpo_blink);

    /* current setting: on or off */   
    if ( TestBit(signal, data) )
    {
        strcat(pBuff, "on");
    }
    else
    {
        strcat(pBuff, "off");
    }

    strcat(pBuff, ", ");


    /* get inversion setting */

    strcat(pBuff, "Inversion:");

    /* get current data from register */
    data = READ_PORT_ULONG((PULONG)gGPIORegs.gpi_inv);

    /* current setting: inverted or noninverted */    
    if ( TestBit(signal, data) )
    {
        strcat(pBuff, "on");
    }
    else
    {
        strcat(pBuff, "off");
    }

    return 0;
}


