// kernel module
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>     // kzalloc

// interrupt & spinlock & event & thread
#include <linux/interrupt.h>
#include <linux/spinlock.h>
//#include <asm/semaphore.h>  // for 2.6.18
#include <linux/semaphore.h>
#include <linux/completion.h>
#include <linux/wait.h>
#include <linux/kthread.h>
//#include <linux/freezer.h>

// time
#include <linux/time.h>
#include <linux/jiffies.h>
#include <linux/delay.h>

// pci & pnp & plateform
//#include <linux/pnp.h>
#include <linux/pci.h>      // about PCI_DEVFN, PCI_SLOT, PCI_FUNC
#include <linux/device.h>
#include <asm/io.h>
#include <asm/uaccess.h>


#define    DMA_ADDR_INVALID    (~(dma_addr_t)0)
//#include <linux/dma-mapping.h>

// beacue we need the serial core
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial_reg.h>
#include <linux/serial.h>
//#include <linux/serialP.h>  kernel 3.5.0 has cancelled it.
#include <linux/serial_core.h>

// our device header
#include "SPtype.h"
#include "DrvDebug.h"   //#define DrvDbgPrint
#include "funcresult.h"
#include "SystemPorting.h"
#include "BusDeviceIO.h"

#include "EUARTDef.h"
#include "HwiEUART.h"
#include "HwdEUART.h"

#include "parport_rdc_A9610.h"
#include "serial_rdc_A9610.h"
#include "serial_rdc_ioctl.h"

// DrvDebug
//
/*#define DBGPRINTF
#ifdef DBGPRINTF
    #define DrvDbgPrint(DebugPrintLevel, format, arg...) printk(KERN_INFO format, ## arg)
    #define DrvDbgPrintIf(bShow, format, arg...) if (bShow) { printk(KERN_INFO format, ## arg); }
#else
    #define DrvDbgPrint(DebugPrintLevel, format, arg...)
#endif*/

//--------
// Driver Infomation

#include "DriverInfo.h"

MODULE_LICENSE("GPL");
MODULE_AUTHOR("kevin.haung@rdc.com.tw");
MODULE_DESCRIPTION("RDC PCI EUART Controller Driver");
MODULE_VERSION(DRIVER_VERSION);

//----
// define module parameters: mp_
//

static int mp_422mode = 1;

MODULE_PARM_DESC(mp_422mode, " 422/485mode: 0:485 1:422");
module_param(mp_422mode, int, 1);

static int mp_UARTSpeedMode = 0;
static int mp_DMAEnable = 1;

MODULE_PARM_DESC(mp_UARTSpeedMode, " UARTSpeedMode: 0 ~ 6");
module_param(mp_UARTSpeedMode, int, 1);

MODULE_PARM_DESC(mp_DMAEnable, " DMAEnable: 0 or 1");
module_param(mp_DMAEnable, int, 1);

// UARTSpeedMode = 0, Standard, ClockRate = 1846153, ClockDIV = 16, MinBaudRateDivisor=1
// UARTSpeedMode = 1, HighSpeed, ClockRate = 24000000, ClockDIV = 16, MinBaudRateDivisor=1
// UARTSpeedMode = 4, Extended1, ClockRate = 1846153, ClockDIV = 8, MinBaudRateDivisor=1
// UARTSpeedMode = 5, Extended2, ClockRate = 24000000, ClockDIV = 13, MinBaudRateDivisor=1
// UARTSpeedMode = 6, Extended2, ClockRate = 50000000, ClockDIV = 1, MinBaudRateDivisor=5

//--------
// define multiple serial pci card for this module supports
// include memory offset.
//

#define is_real_interrupt(irq)    ((irq) != 0)

enum pci_board_num_t {
    pbn_17F3_121C = 0,
    pbn_1BBC_1211,
    pbn_1BBC_1212,
    pbn_1BBC_1213,
    pbn_1BBC_1219,
    pbn_1BBC_121A,
    pbn_1BBC_121B,
    pbn_1BBC_1216,
    pbn_1BBC_1217,
    pbn_1BBC_1215,
    
};

static struct pci_board ThisModule_pci_boards[] = {
    [pbn_17F3_121C] = {
        .vendor         = 0x17F3,
        .device         = 0x121C,
        .subvendor      = PCI_ANY_ID,
        .subdevice      = PCI_ANY_ID,
        .init           = pci_default_init,
        .setup          = pci_default_setup,
        .exit           = pci_default_exit,
        
        .BaseBarForUart = 0,
        .num_ports      = 8,
        .base_baud      = 0, // this is MaxBaudRate
        .uart_offset    = 8,
        .reg_shift      = 0,
        .first_offset   = 0,
        .uartconfigtype = EUART_A9610_232_V1,
        
        .PID            = 0xA9610,
        .BaseBarForDMA  = 3,
        
        .num_parports   = 0,
        .BaseBarForParport_io = 1,
        .BaseBarForParport_iohi = 2,
    },
    
    [pbn_1BBC_1211] = {
        .vendor         = 0x1BBC,
        .device         = 0x1211,
        .subvendor      = PCI_ANY_ID,
        .subdevice      = PCI_ANY_ID,
        .init           = pci_default_init,
        .setup          = pci_default_setup,
        .exit           = pci_default_exit,
        
        .BaseBarForUart = 0,
        .num_ports      = 2,
        .base_baud      = 0, // this is MaxBaudRate
        .uart_offset    = 8,
        .reg_shift      = 0,
        .first_offset   = 0,
        .uartconfigtype = EUART_232_V1,
        
        .PID            = 0,
        .BaseBarForDMA  = 255, // none if 255
        
        .num_parports   = 0,
        .BaseBarForParport_io = 1,
        .BaseBarForParport_iohi = 2,
    },
    
    [pbn_1BBC_1212] = {
        .vendor         = 0x1BBC,
        .device         = 0x1212,
        .subvendor      = PCI_ANY_ID,
        .subdevice      = PCI_ANY_ID,
        .init           = pci_default_init,
        .setup          = pci_default_setup,
        .exit           = pci_default_exit,
        
        .BaseBarForUart = 0,
        .num_ports      = 4,
        .base_baud      = 0, // this is MaxBaudRate
        .uart_offset    = 8,
        .reg_shift      = 0,
        .first_offset   = 0,
        .uartconfigtype = EUART_232_V1,
        
        .PID            = 0,
        .BaseBarForDMA  = 255, // none if 255
        
        .num_parports   = 0,
        .BaseBarForParport_io = 1,
        .BaseBarForParport_iohi = 2,
    },
    
    [pbn_1BBC_1213] = {
        .vendor         = 0x1BBC,
        .device         = 0x1213,
        .subvendor      = PCI_ANY_ID,
        .subdevice      = PCI_ANY_ID,
        .init           = pci_default_init,
        .setup          = pci_default_setup,
        .exit           = pci_default_exit,
        
        .BaseBarForUart = 0,
        .num_ports      = 8,
        .base_baud      = 0, // this is MaxBaudRate
        .uart_offset    = 8,
        .reg_shift      = 0,
        .first_offset   = 0,
        .uartconfigtype = EUART_232_V1,
        
        .PID            = 0,
        .BaseBarForDMA  = 255, // none if 255
        
        .num_parports   = 0,
        .BaseBarForParport_io = 1,
        .BaseBarForParport_iohi = 2,
    },
    
    [pbn_1BBC_1219] = {
        .vendor         = 0x1BBC,
        .device         = 0x1219,
        .subvendor      = PCI_ANY_ID,
        .subdevice      = PCI_ANY_ID,
        .init           = pci_default_init,
        .setup          = pci_default_setup,
        .exit           = pci_default_exit,
        
        .BaseBarForUart = 0,
        .num_ports      = 2,
        .base_baud      = 0, // this is MaxBaudRate
        .uart_offset    = 8,
        .reg_shift      = 0,
        .first_offset   = 0,
        .uartconfigtype = EUART_422485_V1,
        
        .PID            = 0,
        .BaseBarForDMA  = 255, // none if 255
        
        .num_parports   = 0,
        .BaseBarForParport_io = 1,
        .BaseBarForParport_iohi = 2,
    },
    
    [pbn_1BBC_121A] = {
        .vendor         = 0x1BBC,
        .device         = 0x121A,
        .subvendor      = PCI_ANY_ID,
        .subdevice      = PCI_ANY_ID,
        .init           = pci_default_init,
        .setup          = pci_default_setup,
        .exit           = pci_default_exit,
        
        .BaseBarForUart = 0,
        .num_ports      = 4,
        .base_baud      = 0, // this is MaxBaudRate
        .uart_offset    = 8,
        .reg_shift      = 0,
        .first_offset   = 0,
        .uartconfigtype = EUART_422485_V1,
        
        .PID            = 0,
        .BaseBarForDMA  = 255, // none if 255
        
        .num_parports   = 0,
        .BaseBarForParport_io = 1,
        .BaseBarForParport_iohi = 2,
    },
    
    [pbn_1BBC_121B] = {
        .vendor         = 0x1BBC,
        .device         = 0x121B,
        .subvendor      = PCI_ANY_ID,
        .subdevice      = PCI_ANY_ID,
        .init           = pci_default_init,
        .setup          = pci_default_setup,
        .exit           = pci_default_exit,
        
        .BaseBarForUart = 0,
        .num_ports      = 8,
        .base_baud      = 0, // this is MaxBaudRate
        .uart_offset    = 8,
        .reg_shift      = 0,
        .first_offset   = 0,
        .uartconfigtype = EUART_422485_V1,
        
        .PID            = 0,
        .BaseBarForDMA  = 255, // none if 255
        
        .num_parports   = 0,
        .BaseBarForParport_io = 1,
        .BaseBarForParport_iohi = 2,
    },
    
    [pbn_1BBC_1216] = {
        .vendor         = 0x1BBC,
        .device         = 0x1216,
        .subvendor      = PCI_ANY_ID,
        .subdevice      = PCI_ANY_ID,
        .init           = pci_default_init,
        .setup          = pci_default_setup,
        .exit           = pci_default_exit,
        
        .BaseBarForUart = 0,
        .num_ports      = 2,
        .base_baud      = 0, // this is MaxBaudRate
        .uart_offset    = 8,
        .reg_shift      = 0,
        .first_offset   = 0,
        .uartconfigtype = EUART_232_V1,
        
        .PID            = 0,
        .BaseBarForDMA  = 255, // none if 255
        
        .num_parports   = 1,
        .BaseBarForParport_io = 1,
        .BaseBarForParport_iohi = 2,
    },
    
    [pbn_1BBC_1217] = {
        .vendor         = 0x1BBC,
        .device         = 0x1217,
        .subvendor      = PCI_ANY_ID,
        .subdevice      = PCI_ANY_ID,
        .init           = pci_default_init,
        .setup          = pci_default_setup,
        .exit           = pci_default_exit,
        
        .BaseBarForUart = 0,
        .num_ports      = 4,
        .base_baud      = 0, // this is MaxBaudRate
        .uart_offset    = 8,
        .reg_shift      = 0,
        .first_offset   = 0,
        .uartconfigtype = EUART_232_V1,
        
        .PID            = 0,
        .BaseBarForDMA  = 255, // none if 255
        
        .num_parports   = 1,
        .BaseBarForParport_io = 1,
        .BaseBarForParport_iohi = 2,
    },
    
    [pbn_1BBC_1215] = {
        .vendor         = 0x1BBC,
        .device         = 0x1215,
        .subvendor      = PCI_ANY_ID,
        .subdevice      = PCI_ANY_ID,
        .init           = pci_default_init,
        .setup          = pci_default_setup,
        .exit           = pci_default_exit,
        
        .BaseBarForUart = 0,
        .num_ports      = 0,
        .base_baud      = 0, // this is MaxBaudRate
        .uart_offset    = 8,
        .reg_shift      = 0,
        .first_offset   = 0,
        .uartconfigtype = EUART_232_V1,
        
        .PID            = 0,
        .BaseBarForDMA  = 255, // none if 255
        
        .num_parports   = 1,
        .BaseBarForParport_io = 1,
        .BaseBarForParport_iohi = 2,
    },
};


//--------
// define pci device table or this module supports

static const struct pci_device_id pci_device_id_table[] = {
    { 0x17F3, 0x121C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_17F3_121C},
    { 0x1BBC, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_1BBC_1211},
    { 0x1BBC, 0x1212, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_1BBC_1212},
    { 0x1BBC, 0x1213, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_1BBC_1213},
    { 0x1BBC, 0x1219, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_1BBC_1219},
    { 0x1BBC, 0x121A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_1BBC_121A},
    { 0x1BBC, 0x121B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_1BBC_121B},
    { 0x1BBC, 0x1216, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_1BBC_1216},
    { 0x1BBC, 0x1217, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_1BBC_1217},
    { 0x1BBC, 0x1215, PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_1BBC_1215},
    { }    /* terminate list */
};
MODULE_DEVICE_TABLE(pci, pci_device_id_table);

//--------
// define this pci driver

static struct pci_driver ThisModule_pci_driver = {
    .name                       = DRIVER_NAME,
    .id_table                   = pci_device_id_table,
    .probe                      = pcidevice_probe,
    .remove                     = pcidevice_remove,
#ifdef CONFIG_PM
    .suspend                    = pcidevice_suspend,
    .resume                     = pcidevice_resume,
#endif
};


//--------
// define sharing interrupt list
// following reference examples, we use shared_irq_list to do a manual sharing interrupt handle.

struct shared_irq_list {
    spinlock_t          lock;
    unsigned int        referencecount;   // reference count
    struct list_head    head;
};

// struct list_head    shared_irq_entry;

static struct shared_irq_list ThisModule_shared_irq_lists[NR_IRQS];
    // the index is irq number(pcidev->irq).
    /*
    * NR_IRQS is the upper bound of how many interrupts can be handled
    * in the platform.
    */


//--------
// define uart_driver (tty) of the serial core.
//
#define NR_PORTS        32  // totoal number of ports for this pci module.
                            // this pci module can been load by more the one pci device.
                            // each device may have more the one port.
                            // ex: four 8ports pci card.
#define ThisUart_MAJOR  30
#define ThisUart_MINOR  0

static struct uart_driver ThisModule_uart_driver = {
    .owner          = THIS_MODULE,
    .driver_name    = "serial_rdc",
    .dev_name       = "ttyRDC",
    .major          = ThisUart_MAJOR,
    .minor          = ThisUart_MINOR,
    .nr             = NR_PORTS,
    .cons           = NULL,
};

//--------
// define uart_config for this module
//

static const struct uart_config ThisModule_uart_config[] = {
    [PORT_UNKNOWN] = {
        .name        = "unknown",
        .fifo_size    = 1,
        .tx_loadsz    = 1,
    },
    [EUART_232_V1] = {
        .name        = "EUART_232_V1",
        .fifo_size    = 64,
        .tx_loadsz    = 14,
    },
    [EUART_422485_V1] = {
        .name        = "EUART_422485_V1",
        .fifo_size    = 64,
        .tx_loadsz    = 14,
    },
    [EUART_A9610_232_V1] = {
        .name        = "EUART_A9610_232_V1",
        .fifo_size    = 64,
        .tx_loadsz    = 14,
    },
    [EUART_A9610_422485_V1] = {
        .name        = "EUART_A9610_422485_V1",
        .fifo_size    = 64,
        .tx_loadsz    = 14,
    },
};

//--------
// define my uart_ops for the serial core.
//

static struct uart_ops ThisModule_uart_ops = {
    .release_port   = uart_ops_release_port,
    .request_port   = uart_ops_request_port,
    .config_port    = uart_ops_config_port,
    .type           = uart_ops_type,
    
    .startup        = uart_ops_startup,
    .shutdown       = uart_ops_shutdown,
    .pm             = uart_ops_pm,
    
    .set_termios    = uart_ops_set_termios,
    
    .set_mctrl      = uart_ops_set_mctrl,
    .get_mctrl      = uart_ops_get_mctrl,
    .tx_empty       = uart_ops_tx_empty,
    .start_tx       = uart_ops_start_tx,
    .stop_tx        = uart_ops_stop_tx,
    .stop_rx        = uart_ops_stop_rx,
    .enable_ms      = uart_ops_enable_ms,
    .break_ctl      = uart_ops_break_ctl,
    .ioctl          = uart_ops_ioctl,
    
};

//--------
// define uart_device for this module
//

static DEFINE_MUTEX(ThisModule_uart_mutex);

static struct uart_port_device ThisModule_uart_port_devices[NR_PORTS];
    // declare static uart_devices
    // the index is minor number.
    // the serial core use uart_port.line as port minor.




// Module Block
//

struct workqueue_struct  *pWorkQueueA;
struct completion        sCompletionA;
struct work_struct       sWorkA;

struct workqueue_struct  *pWorkQueueB;
struct work_struct       sWorkB;

void
RoutineWorkQueueA(
            struct work_struct *    data
    )
{
    DrvDbgPrint(0, "RoutineWorkQueueA Entry \n");
    
    DrvDbgPrint(0, "    pid:%u (%s) state:%i \n", current->pid, current->comm, current->state);
    
    while (1)
    {
        wait_for_completion_interruptible(&sCompletionA);
        
        break;
    }
    
    DrvDbgPrint(0, "RoutineWorkQueueA Exit \n");
}

void
RoutineWorkQueueB(
            struct work_struct *    data
    )
{
    DrvDbgPrint(0, "RoutineWorkQueueB Entry \n");
    
    DrvDbgPrint(0, "    pid:%u (%s) state:%i \n", current->pid, current->comm, current->state);
    
    DrvDbgPrint(0, "RoutineWorkQueueB Exit \n");
}

module_init(Module_init);
module_exit(Module_exit);

int __init
Module_init(void)
{
    int                         retval;
                                // return -ERRNO -EFAULT
    
    //int                         reterr;
                                // check if (reterr < 0)
    
    //unsigned int                result;
                                // check if (result != 0)
    
    unsigned int                i;
    
    DrvDbgPrint(0, "Module_init %s \n", DRIVER_NAME);
    
    DrvDbgPrint(0, "    pid:%u (%s) state:%i \n", current->pid, current->comm, current->state);
    
    DrvDbgPrint(0, "    sizeof(char):%lu sizeof(int):%lu sizeof(long):%lu \n", sizeof(char), sizeof(int), sizeof(long));
    DrvDbgPrint(0, "    sizeof(SHORT):%lu sizeof(LONG):%lu sizeof(INT):%lu \n", sizeof(SHORT), sizeof(LONG), sizeof(INT));
    DrvDbgPrint(0, "    sizeof(void*):%lu sizeof(UINTPTR):%lu \n", sizeof(void*), sizeof(UINTPTR));
    
    DrvDbgPrint(0, "    mp_UARTSpeedMode:%i mp_DMAEnable:%i \n", mp_UARTSpeedMode, mp_DMAEnable);
    
    // initialize module level (global) data.
    
    retval = 0;
    
    for (i = 0; i < NR_IRQS; i++)
    {
        spin_lock_init(&ThisModule_shared_irq_lists[i].lock);
        ThisModule_shared_irq_lists[i].referencecount = 0;
        INIT_LIST_HEAD(&ThisModule_shared_irq_lists[i].head);
    }
    
    for (i = 0; i < NR_PORTS; i++)
    {
        ThisModule_uart_port_devices[i].pcidev = NULL;
        ThisModule_uart_port_devices[i].minor = i;
    }
    
    retval = uart_register_driver(&ThisModule_uart_driver);
    if (retval < 0)
    {
        DrvDbgPrint(0, "uart_register_driver failed\n");
        DrvDbgPrint(0, "Module_init failed\n");
        return retval;
    }
    
    retval = pci_register_driver(&ThisModule_pci_driver);
    if (retval < 0)
    {
        DrvDbgPrint(0, "pci_register_driver failed\n");
        DrvDbgPrint(0, "Module_init failed\n");
        uart_unregister_driver(&ThisModule_uart_driver);
        return retval;
    }
    
    // WorkQueue test code
    if (0)
    {
        INIT_WORK(&sWorkA, RoutineWorkQueueA);
        INIT_WORK(&sWorkB, RoutineWorkQueueB);
        
        pWorkQueueA = create_workqueue("WorkQueueA"); // create_singlethread_workqueue
        pWorkQueueB = create_workqueue("WorkQueueB");
        
        init_completion(&sCompletionA);
        
        queue_work(pWorkQueueA, &sWorkA);
        queue_work(pWorkQueueB, &sWorkB);
    }
    
    return 0;
}

void __exit
Module_exit(void)
{
    DrvDbgPrint(0, "Module_exit entry\n");
    DrvDbgPrint(0, "    pid:%u (%s) state:%i \n", current->pid, current->comm, current->state);
    
    // WorkQueue test code
    if (0)
    {
        complete(&sCompletionA);
        
        flush_workqueue(pWorkQueueA);
        flush_workqueue(pWorkQueueB);
        
        destroy_workqueue(pWorkQueueA);
        destroy_workqueue(pWorkQueueB);
    }
    
    pci_unregister_driver(&ThisModule_pci_driver);
    uart_unregister_driver(&ThisModule_uart_driver);
    DrvDbgPrint(0, "Module_exit exit\n");
}

unsigned int
ReadPCIConfiguration(
            unsigned int        BusNumber,
            unsigned int        DeviceNumber,
            unsigned int        FunctionNumber,
            unsigned int        Offset,
            unsigned int        Length, // 1 , 2 , 4
            void *              pBuffer
    )
{
    unsigned int                funcresult;
    
    unsigned int                result;
    
    unsigned int                devfn;
    
    struct  pci_dev             *hpci_dev;
        
    funcresult = TRUE;
    
    hpci_dev = NULL;
    
    devfn = PCI_DEVFN(DeviceNumber, FunctionNumber);
    
    //struct pci_dev * pci_get_bus_and_slot(unsigned int bus, unsigned int devfn)
    hpci_dev = pci_get_bus_and_slot(
        BusNumber,
        devfn
        );
    if (hpci_dev == NULL)
    {
        funcresult = FALSE;
        goto funcexit;
    }
    
    
    switch (Length)
    {
        case 1:
        {
            // pci_read_config_byte(struct pci_dev *dev, int where, u8 *val);
            result = pci_read_config_byte(
                hpci_dev,
                Offset,
                pBuffer
                );
            if (result != PCIBIOS_SUCCESSFUL)
            {
                funcresult = FALSE;
                goto funcexit;
            }
        }
        break;
        
        case 2:
        {
            result = pci_read_config_word(
                hpci_dev,
                Offset,
                pBuffer
                );
            if (result != PCIBIOS_SUCCESSFUL)
            {
                funcresult = FALSE;
                goto funcexit;
            }
        }
        break;
        
        case 4:
        {
            result = pci_read_config_dword(
                hpci_dev,
                Offset,
                pBuffer
                );
            if (result != PCIBIOS_SUCCESSFUL)
            {
                funcresult = FALSE;
                goto funcexit;
            }
        }
        break;
        
        default:
        {
            funcresult = FALSE;
            goto funcexit;
        }
        break;
    }
    
    funcresult = TRUE;
    
funcexit:

    if (hpci_dev != NULL)
    {
        pci_dev_put(hpci_dev);
    }
    
    if (funcresult == FALSE)
    {
        DrvDbgPrint(0, "    ReadPCIConfiguration result == FALSE \n");
    }
    
    return funcresult;
}


unsigned int
WritePCIConfiguration(
            unsigned int        BusNumber,
            unsigned int        DeviceNumber,
            unsigned int        FunctionNumber,
            unsigned int        Offset,
            unsigned int        Length, // 1 , 2 , 4
            void *              pBuffer
    )
{
    unsigned int                funcresult;
    
    unsigned int                result;
    
    unsigned int                devfn;
    
    struct  pci_dev             *hpci_dev;
        
    funcresult = TRUE;
    
    hpci_dev = NULL;
    
    devfn = PCI_DEVFN(DeviceNumber, FunctionNumber);
    
    //struct pci_dev * pci_get_bus_and_slot(unsigned int bus, unsigned int devfn)
    hpci_dev = pci_get_bus_and_slot(
        BusNumber,
        devfn
        );
    if (hpci_dev == NULL)
    {
        funcresult = FALSE;
        goto funcexit;
    }
    
    
    switch (Length)
    {
        case 1:
        {
            // pci_write_config_byte(struct pci_dev *dev, int where, u8 *val);
            result = pci_write_config_byte(
                hpci_dev,
                Offset,
                *(u8*)pBuffer
                );
            if (result != PCIBIOS_SUCCESSFUL)
            {
                funcresult = FALSE;
                goto funcexit;
            }
        }
        break;
        
        case 2:
        {
            result = pci_write_config_word(
                hpci_dev,
                Offset,
                *(u16*)pBuffer
                );
            if (result != PCIBIOS_SUCCESSFUL)
            {
                funcresult = FALSE;
                goto funcexit;
            }
        }
        break;
        
        case 4:
        {
            result = pci_write_config_dword(
                hpci_dev,
                Offset,
                *(u32*)pBuffer
                );
            if (result != PCIBIOS_SUCCESSFUL)
            {
                funcresult = FALSE;
                goto funcexit;
            }
        }
        break;
        
        default:
        {
            funcresult = FALSE;
            goto funcexit;
        }
        break;
    }
    
    funcresult = TRUE;
    
funcexit:

    if (hpci_dev != NULL)
    {
        pci_dev_put(hpci_dev);
    }
    
    if (funcresult == FALSE)
    {
        DrvDbgPrint(0, "    WritePCIConfiguration result == FALSE \n");
    }
    
    return funcresult;
}

void
receive_chars(
    struct uart_port *          uartport,
    unsigned int *              status
    )
{
    struct tty_struct           *tty;
    unsigned char               ch;
    unsigned char               lsr;
    int                         max_count = 256;
    char                        flagchar;
    
    //
    // initialize local variables
    //
    
    //
    // check parameters
    //
    
    //
    // function body
    //
    
    tty = uartport->state->port.tty;
    
    lsr = *status;

    do
    {
        ch = uart_serial_in(uartport, UART_RX);
        uartport->icount.rx++;
        
        flagchar = TTY_NORMAL;
        
        if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS))
        {
            /*
             * For statistics only
             */
            if (lsr & UART_LSR_BI)
            {
                lsr &= ~(UART_LSR_FE | UART_LSR_PE);
                uartport->icount.brk++;
                /*
                 * We do the SysRQ and SAK checking
                 * here because otherwise the break
                 * may get masked by ignore_status_mask
                 * or read_status_mask.
                 */
                if (uart_handle_break(uartport))
                {
                    goto ignore_char;
                }
            }
            else if (lsr & UART_LSR_PE)
            {
                uartport->icount.parity++;
            }
            else if (lsr & UART_LSR_FE)
            {
                uartport->icount.frame++;
            }
            
            if (lsr & UART_LSR_OE)
            {
                uartport->icount.overrun++;
            }

            /*
             * Mask off conditions which should be ignored.
             */
            lsr &= uartport->read_status_mask;

            if (lsr & UART_LSR_BI)
            {
                flagchar = TTY_BREAK;
            }
            else if (lsr & UART_LSR_PE)
            {
                flagchar = TTY_PARITY;
            }
            else if (lsr & UART_LSR_FE)
            {
                flagchar = TTY_FRAME;
            }
        }
        
        if (uart_handle_sysrq_char(uartport, ch))
        {
            goto ignore_char;
        }

        uart_insert_char(uartport, lsr, UART_LSR_OE, ch, flagchar);
        
    ignore_char:
        lsr = uart_serial_in(uartport, UART_LSR);
        
    //}while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (max_count-- > 0));
        // we dont use 8250 version becasue 8250 handle Intel 82571
        // that it has a Serial Over Lan device that will set UART_LSR_BI
        // without setting UART_LSR_DR when it receives a break.
    }while ((lsr & (UART_LSR_DR)) && (max_count-- > 0));    // xr17c15x version
    
    spin_unlock(&uartport->lock);
    
    tty_flip_buffer_push(tty);
    
    spin_lock(&uartport->lock);
    
    *status = lsr;
    
    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    //return retval;
    return;        
}


void
transmit_chars(
    struct uart_port *          uartport
    )
{
    struct uart_port_device     *uartportdevice;
    
    struct circ_buf             *xmit;
    int                         count;
    
    //
    // initialize local variables
    //
    
    //
    // check parameters
    //
    
    //
    // function body
    //
    
    uartportdevice = (struct uart_port_device *)uartport;
    
    xmit = &uartport->state->xmit;
    
    if (uartport->x_char)
    {   // send one xon or xoff
        uart_serial_out(uartport, UART_TX, uartport->x_char);
        uartport->icount.tx++;
        uartport->x_char = 0;
        return;
    }
    
    if (uart_tx_stopped(uartport))
    {
        uart_ops_stop_tx(uartport);
        return;
    }
    
    if (uart_circ_empty(xmit))
    {
        uart_ops_stop_tx(uartport);
        return;
    }
    
    count = uartportdevice->tx_loadsz;
    do
    {
        uart_serial_out(uartport, UART_TX, xmit->buf[xmit->tail]);
        xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
        uartport->icount.tx++;
        if (uart_circ_empty(xmit))
        {
            break;
        }
        count -= 1;
    }while (count > 0);
    
    if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
    {
        uart_write_wakeup(uartport);
    }

    if (uart_circ_empty(xmit))
    {
        uart_ops_stop_tx(uartport);
    }
    
    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    //return retval;
    return;
}


int
check_modem_status(
    struct uart_port *          uartport
    )
{
    unsigned int                retval;
    
    unsigned char               msr;
    
    //
    // initialize local variables
    //
    
    //
    // check parameters
    //
    
    //
    // function body
    //

    msr = uart_serial_in(uartport, UART_MSR);
    
    retval = msr;

    if ((msr & UART_MSR_ANY_DELTA) == 0)
    {
        goto funcexit;
    }

    if (msr & UART_MSR_TERI)
        uartport->icount.rng++;
    if (msr & UART_MSR_DDSR)
        uartport->icount.dsr++;
    if (msr & UART_MSR_DDCD)
        uart_handle_dcd_change(uartport, msr & UART_MSR_DCD);
    if (msr & UART_MSR_DCTS)
        uart_handle_cts_change(uartport, msr & UART_MSR_CTS);

    wake_up_interruptible(&uartport->state->port.delta_msr_wait);
    
    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    return retval;
    //return;
}

// This handles the interrupt from one port.
void
uart_handle_port(
    struct uart_port *          uartport
    )
{
    unsigned int                lsr;
    
    //
    // initialize local variables
    //
    
    //
    // check parameters
    //
    
    //
    // function body
    //

    lsr = uart_serial_in(uartport, UART_LSR);

    //if (lsr & (UART_LSR_DR | UART_LSR_BI))  // 8250 version with Intel 82571 see receive_chars()
    if (lsr & UART_LSR_DR)                // xr17c15x version
    {
        receive_chars(uartport, &lsr);
    }
    
    check_modem_status(uartport);
    
    if (lsr & UART_LSR_THRE)
    {
        transmit_chars(uartport);
    }
    
    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    //return retval;
    return;
}


void
receive_chars_DMA(
    struct uart_port *          uartport,
    unsigned int                NumberRxChar,
    PEUARTCharAndStatus         pCharAndStatus
    )
{
    unsigned int                i;
    
    struct uart_port_device     *uartportdevice;
    
    struct tty_struct           *tty;
    unsigned char               ch;
    unsigned char               lsr;
    int                         max_count = 256;
    char                        flagchar;
    unsigned int                ignore_char;
    
    //
    // initialize local variables
    //
    
    uartportdevice = (struct uart_port_device *)uartport;
    
    //
    // check parameters
    //
    
    //
    // function body
    //
    
    tty = uartport->state->port.tty;
    
    if (uartportdevice->bops_stop_rx == TRUE)
    {
        return;
    }
    
    for (i = 0; i < NumberRxChar; i++)
    {
        lsr = pCharAndStatus[i].CharStatus;
        ch = pCharAndStatus[i].Char;
        uartport->icount.rx++;
        
        flagchar = TTY_NORMAL;
        
        ignore_char = FALSE;
        if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS))
        {
            /*
             * For statistics only
             */
            if (lsr & UART_LSR_BI)
            {
                lsr &= ~(UART_LSR_FE | UART_LSR_PE);
                uartport->icount.brk++;
                /*
                 * We do the SysRQ and SAK checking
                 * here because otherwise the break
                 * may get masked by ignore_status_mask
                 * or read_status_mask.
                 */
                if (uart_handle_break(uartport))
                {
                    ignore_char = TRUE;
                }
            }
            else if (lsr & UART_LSR_PE)
            {
                uartport->icount.parity++;
            }
            else if (lsr & UART_LSR_FE)
            {
                uartport->icount.frame++;
            }
            
            if (ignore_char == FALSE)
            {
                if (lsr & UART_LSR_OE)
                {
                    uartport->icount.overrun++;
                }
    
                /*
                 * Mask off conditions which should be ignored.
                 */
                lsr &= uartport->read_status_mask;
    
                if (lsr & UART_LSR_BI)
                {
                    flagchar = TTY_BREAK;
                }
                else if (lsr & UART_LSR_PE)
                {
                    flagchar = TTY_PARITY;
                }
                else if (lsr & UART_LSR_FE)
                {
                    flagchar = TTY_FRAME;
                }
            }
        }
        
        if (ignore_char == FALSE)
        {
            if (uart_handle_sysrq_char(uartport, ch))
            {
                ignore_char = TRUE;
            }
        }
        
        if (ignore_char == FALSE)
        {
            uart_insert_char(uartport, lsr, UART_LSR_OE, ch, flagchar);
        }
    }
    
    spin_unlock(&uartport->lock);
    
    tty_flip_buffer_push(tty);
    
    spin_lock(&uartport->lock);
    
    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    //return retval;
    return;        
}


void
transmit_chars_DMA(
    struct uart_port *          uartport
    )
{
    unsigned int                i;
    
    struct uart_port_device     *uartportdevice;
    
    struct circ_buf             *xmit;
    int                         count;
    
    UCHAR                       DataBuffer[128];
    
    //
    // initialize local variables
    //
    
    //
    // check parameters
    //
    
    //
    // function body
    //
    
    uartportdevice = (struct uart_port_device *)uartport;
    
    xmit = &uartport->state->xmit;
    
    if (uartportdevice->bops_stop_tx == TRUE)
    {
        //DrvDbgPrint(0, "bops_stop_tx == TRUE \n");
        return;
    }
    
    if (uartport->x_char)
    {   // send one xon or xoff
        DrvDbgPrint(0, "  send xon/xoff:0x%x \n", uartport->x_char);
        uart_serial_out(uartport, UART_TX, uartport->x_char);
        HwdEUART_DMAWriteTxBuffer(
            uartportdevice->hHwdEUART,
            &uartport->x_char,
            1
            );
        uartport->icount.tx++;
        uartport->x_char = 0;
        return;
    }
    
    if (uart_tx_stopped(uartport))
    {
        uart_ops_stop_tx(uartport);
        //DrvDbgPrint(0, "uart_tx_stopped(uartport) \n");
        return;
    }
    
    if (uart_circ_empty(xmit))
    {
        uart_ops_stop_tx(uartport);
        //DrvDbgPrint(0, "uart_circ_empty(xmit) \n");
        return;
    }
    
    count = 0;
    for (i = 0; i < uartportdevice->tx_loadsz; i++)
    {
        DataBuffer[i] = xmit->buf[xmit->tail];
        //DrvDbgPrint(0, "DataBuffer[%u] = 0x%x \n", i, DataBuffer[i]);
        xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
        count += 1;
        if (uart_circ_empty(xmit))
        {
            break;
        }
    }
    
    //DrvDbgPrint(0, "count = %u \n", count);
    HwdEUART_DMAWriteTxBuffer(
        uartportdevice->hHwdEUART,
        DataBuffer,
        count
        );
    
    uartport->icount.tx += count;
    
    if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
    {
        uart_write_wakeup(uartport);
    }

    if (uart_circ_empty(xmit))
    {
        uart_ops_stop_tx(uartport);
    }
    
    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    //return retval;
    return;
}


int
check_modem_status_DMA(
    struct uart_port *          uartport,
    unsigned char               msr
    )
{
    unsigned int                retval;
    
    //
    // initialize local variables
    //
    
    //
    // check parameters
    //
    
    //
    // function body
    //

    retval = msr;

    if ((msr & UART_MSR_ANY_DELTA) == 0)
    {
        goto funcexit;
    }

    if (msr & UART_MSR_TERI)
        uartport->icount.rng++;
    if (msr & UART_MSR_DDSR)
        uartport->icount.dsr++;
    if (msr & UART_MSR_DDCD)
        uart_handle_dcd_change(uartport, msr & UART_MSR_DCD);
    if (msr & UART_MSR_DCTS)
        uart_handle_cts_change(uartport, msr & UART_MSR_CTS);

    wake_up_interruptible(&uartport->state->port.delta_msr_wait);
    
    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    return retval;
    //return;
}


void
uart_handle_port_DMA(
    struct uart_port *          uartport,
    unsigned int                NumberRxChar,
    PEUARTCharAndStatus         pCharAndStatus,
    unsigned char               msr
    )
{
    struct uart_port_device     *uartportdevice;
    
    ULONG                       bAllEmpty;
    //
    // initialize local variables
    //
    
    uartportdevice = (struct uart_port_device *)uartport;
    
    //
    // check parameters
    //
    
    //
    // function body
    //

    if (NumberRxChar != 0)
    {
        receive_chars_DMA(uartport, NumberRxChar, pCharAndStatus);
    }
    
    check_modem_status_DMA(uartport, msr);
    
    HwdEUART_DMAAllEmpty( //or HwdEUART_DMATxEmpty
        uartportdevice->hHwdEUART,
        &bAllEmpty
        );
    if (bAllEmpty)
    {
        //DrvDbgPrint(0, "    bAllEmpty \n");
        transmit_chars_DMA(uartport);
    }
    
    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    //return retval;
    return;
}


// callback function for pci_driver

irqreturn_t
IRQHandler(
            int                 irq,
            void *              devicedata
                // in general, devicedata is device context.
                // each device will register its IRQHandler.
                // OS handle interrupt by polling chained IRQHandler.
                // If a interrupt occurs, OS will pass irq and devicedata to IRQHandler.
                // according the devicedata(device context_, IRQHandler will know which device is handled this time.
                    
                // in here, devicedata is this_shared_irq_list.
                // Module will register the individual irq used by all devices mudule supported.
                // If there are two devices and they IRQ are different, two IRQHandler will be registered.
                // If there are two devices and they IRQ are the same, one IRQHandler will be registered and be shared.
                // If a interrupt occurs, OS will pass irq and its this_shared_irq_list to IRQHandler.
                // according the devicedata(this_shared_irq_list), IRQHandler will know which irq is handled this time.
                // If two devices shared this irq(devices are linked to shared_irq_list),
                // IRQHandler will detect which device on shared_irq_list issue this interrupt.
                
                // maybe, we can change this_shared_irq_list to device context.
                // but why 8250.c use this shared irq list to handle interrupt?
    )
{
    int                         retval;
    
    struct shared_irq_list      *this_shared_irq_list;
    struct uart_port_device     *uartportdevice;
    struct uart_port            *uartport;
    struct pci_driver_data      *this_driver_data;
    struct pci_board            *this_board;
    
    
    unsigned int                iir;
    unsigned char               msr;
    
    //
    // initialize local variables
    //
    
    //
    // check parameters
    //
    
    //
    // function body
    //
    
    this_shared_irq_list = (struct shared_irq_list*) devicedata;
    
    retval = IRQ_NONE;
    
    spin_lock(&this_shared_irq_list->lock);
    
    if (list_empty(&this_shared_irq_list->head))
    {
        //DrvDbgPrint(0, "IRQHandler this_shared_irq_list emtpy \n");
    }
    else
    {
        list_for_each_entry(uartportdevice, &this_shared_irq_list->head, shared_irq_entry)
        {
            this_driver_data = pci_get_drvdata(uartportdevice->pcidev);
            this_board = this_driver_data->this_board;
            
            uartport = &uartportdevice->port;
            
            if (this_board->PID == 0xA9610 && uartportdevice->DMAEnable == TRUE)
            {
                ULONG                  IndexStart;
                ULONG                  IndexEnd;
                ULONG                  bFull;
                
                ULONG                  bInterrupt;
                HwdEUARTInterrupt      sInterrupt;
                HwdEUARTVolatileInfo   sVolatileInfo;
                
                PINTERRUPT_EXTENSION   pInterruptExtension;
                
                memset(&sInterrupt, 0, sizeof(sInterrupt));
                memset(&sVolatileInfo, 0, sizeof(sVolatileInfo));
                
                spin_lock(&uartport->lock);
                
                // DrvDbgPrint(0, "IRQHandler: portid:%u minor:%i \n", uartportdevice->portid, uartportdevice->minor);
                
                HwdEUART_DMAISR(
                    uartportdevice->hHwdEUART,
                    &bInterrupt,
                    &sInterrupt,
                    &sVolatileInfo
                    );
                if (bInterrupt)
                {
                    /*if (0)
                    {
                        DrvDbgPrint(0, "IRQHandler: sInterrupt \n");
                        DrvDbgPrint(0, "   TxSegment0:%u TxSegment1:%u \n",
                            sVolatileInfo.sEUARTDMAInterruptStatus.TxSegment0,
                            sVolatileInfo.sEUARTDMAInterruptStatus.TxSegment1
                            );
                        DrvDbgPrint(0, "   RxSegment0:%u RxSegment1:%u \n",
                            sVolatileInfo.sEUARTDMAInterruptStatus.RxSegment0,
                            sVolatileInfo.sEUARTDMAInterruptStatus.RxSegment1
                            );
                        DrvDbgPrint(0, "   ModemStatus:%u RxSegmentOverrun:%u UARTOverrun:%u\n",
                            sVolatileInfo.sEUARTDMAInterruptStatus.ModemStatus,
                            sVolatileInfo.sEUARTDMAInterruptStatus.RxSegmentOverrun,
                            sVolatileInfo.sEUARTDMAInterruptStatus.UARTOverrun
                            );
                    }*/
                    if (sVolatileInfo.sEUARTDMAInterruptStatus.RxSegmentOverrun == 1 ||
                        sVolatileInfo.sEUARTDMAInterruptStatus.UARTOverrun == 1)
                    {
                        DrvDbgPrint(0, "   RxSegmentOverrun:%u UARTOverrun:%u\n",
                            sVolatileInfo.sEUARTDMAInterruptStatus.RxSegmentOverrun,
                            sVolatileInfo.sEUARTDMAInterruptStatus.UARTOverrun
                            );
                    }
                    /*{ // ISRRoutine
                        msr = 0;
                        if (sVolatileInfo.sEUARTModemStatus.CTSChange)
                        {
                            msr |= 0x01;
                        }
                        
                        if (sVolatileInfo.sEUARTModemStatus.DSRChange)
                        {
                            msr |= 0x02;
                        }
                        
                        if (sVolatileInfo.sEUARTModemStatus.RIChange)
                        {
                            msr |= 0x04;
                        }
                        
                        if (sVolatileInfo.sEUARTModemStatus.DCDChange)
                        {
                            msr |= 0x08;
                        }
                        
                        if (sVolatileInfo.sEUARTModemStatus.CTS)
                        {
                            msr |= 0x10;
                        }
                        
                        if (sVolatileInfo.sEUARTModemStatus.DSR)
                        {
                            msr |= 0x20;
                        }
                        
                        if (sVolatileInfo.sEUARTModemStatus.RI)
                        {
                            msr |= 0x40;
                        }
                        
                        if (sVolatileInfo.sEUARTModemStatus.DCD)
                        {
                            msr |= 0x80;
                        }
                        
                        uart_handle_port_DMA(uartport, sVolatileInfo.NumberRxChar, sVolatileInfo.CharAndStatus, msr);
                    }
                    */
                    
                    { //using ISRDPCRoutine
                        pInterruptExtension = &uartportdevice->sInterruptExtension;
                        
                        // check if FIFO Full
                        IndexStart = pInterruptExtension->Interrupt_Start;
                        IndexEnd = pInterruptExtension->Interrupt_End;
                        bFull = FALSE;
                        if (((IndexEnd + 1)%InterruptQueue_MaxCount) == IndexStart)
                        {
                            // overrun
                            bFull = TRUE;
                        }
                        
                        if (bFull == FALSE)
                        {
                            pInterruptExtension->Interrupt_Overrun = FALSE;
                            
                            // put data into FIFO
                            //SPCopyMemory(&pInterruptExtension->losInterruptInfo[IndexEnd], &sInterruptInfo, sizeof(InterruptInfo));
                            pInterruptExtension->losInterrupt[IndexEnd] = sInterrupt;
                            pInterruptExtension->losVolatileInfo[IndexEnd] = sVolatileInfo;
                            
                            // update IndexEnd
                            pInterruptExtension->Interrupt_End = (IndexEnd + 1)%InterruptQueue_MaxCount;
                            pInterruptExtension->Interrupt_Count += 1;
                        }
                        else
                        {
                            // the program should run to here.
                            pInterruptExtension->Interrupt_Overrun = TRUE;
                            
                            DrvDbgPrint(0, "    Interrupt_Overrun \n");
                        }
                        
                        // schedule EvtInterruptDpc
                        tasklet_schedule(&uartportdevice->ISRDPCTasklet);
                    }
                    
                    retval = IRQ_HANDLED;
                }
                
                spin_unlock(&uartport->lock);
            }
            else
            {
                spin_lock(&uartport->lock);
                
                // DrvDbgPrint(0, "IRQHandler: portid:%u minor:%i \n", uartportdevice->portid, uartportdevice->minor);
                
                iir = uart_serial_in(uartport, UART_IIR);
                if (iir & UART_IIR_NO_INT)
                {
                }
                else
                {
                    // maybe, we can upgrade uart_handle_port by means of DPC routine.
                    uart_handle_port(uartport);
                    
                    retval = IRQ_HANDLED;
                }
                
                spin_unlock(&uartport->lock);
            }
        }
    }
    
    spin_unlock(&this_shared_irq_list->lock);
    
    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    return retval;
    //return;
}


void
ISRDPCRoutine(
            UINTPTR             data // moduleportindex
    )
{
    struct uart_port            *uartport;
    struct uart_port_device     *this_port_device;
    
    PINTERRUPT_EXTENSION        pInterruptExtension;
    
    unsigned long               flags;
                                //irq flags use unsigned long in linux
    
    ULONG                       IndexStart;
    ULONG                       IndexEnd;
    ULONG                       bEmpty;
    
    HwdEUARTInterrupt           sInterrupt = {0};    // to remove warning message
    HwdEUARTVolatileInfo        sVolatileInfo = {0}; // to remove warning message
    
    unsigned char               msr;
    
    //
    // initialize local variables
    //
    
    //DrvDbgPrint(0, "EvtInterruptDpc Entry \n");
    
    //hDevice = AssociatedObject;
    
    //
    // check parameters
    //
    
    //
    // function body
    //
    
    this_port_device = &ThisModule_uart_port_devices[data];
    uartport = (struct uart_port *)this_port_device;
    
    pInterruptExtension = &this_port_device->sInterruptExtension;
    
    bEmpty = FALSE;
    
    while(bEmpty == FALSE)
    {
        //WdfInterruptAcquireLock(Interrupt);
        spin_lock_irqsave(&this_port_device->spinlock_ISRDPCQueue, flags);
        
        // get InterruptVolatileInformation from DpcQueue
        IndexStart = pInterruptExtension->Interrupt_Start;
        IndexEnd = pInterruptExtension->Interrupt_End;
        
        if (pInterruptExtension->Interrupt_Overrun == TRUE)
        {
            DrvDbgPrint(0, "    Interrupt_Overrun \n");
        }
        
        if (IndexStart == IndexEnd)
        {   // empty
            bEmpty = TRUE;
        }
        else
        {
            bEmpty = FALSE;
            
            // get data from FIFO
            //SPCopyMemory(&sInterruptInfo, &pInterruptExtension->losInterruptInfo[IndexStart], sizeof(InterruptInfo));
            sInterrupt = pInterruptExtension->losInterrupt[IndexStart];
            sVolatileInfo = pInterruptExtension->losVolatileInfo[IndexStart];
            
            // update IndexStart
            pInterruptExtension->Interrupt_Start = (IndexStart + 1)%InterruptQueue_MaxCount;
            pInterruptExtension->Interrupt_Count -= 1;
        }
        
        //WdfInterruptReleaseLock(Interrupt);
        spin_unlock_irqrestore(&this_port_device->spinlock_ISRDPCQueue, flags);
        
        if (bEmpty == TRUE)
        {
            break;
        }
        
        //SPSpinLock_AcquireAtDpcLevel(&this_port_device->sSPSpinLock_candev, &flags);
        spin_lock_irqsave(&uartport->lock, flags);
        
        // handle InterruptVolatileInformation
        {
            msr = 0;
            if (sVolatileInfo.sEUARTModemStatus.CTSChange)
            {
                msr |= 0x01;
            }
            
            if (sVolatileInfo.sEUARTModemStatus.DSRChange)
            {
                msr |= 0x02;
            }
            
            if (sVolatileInfo.sEUARTModemStatus.RIChange)
            {
                msr |= 0x04;
            }
            
            if (sVolatileInfo.sEUARTModemStatus.DCDChange)
            {
                msr |= 0x08;
            }
            
            if (sVolatileInfo.sEUARTModemStatus.CTS)
            {
                msr |= 0x10;
            }
            
            if (sVolatileInfo.sEUARTModemStatus.DSR)
            {
                msr |= 0x20;
            }
            
            if (sVolatileInfo.sEUARTModemStatus.RI)
            {
                msr |= 0x40;
            }
            
            if (sVolatileInfo.sEUARTModemStatus.DCD)
            {
                msr |= 0x80;
            }
            
            uart_handle_port_DMA(uartport, sVolatileInfo.NumberRxChar, sVolatileInfo.CharAndStatus, msr);
        }
        
        spin_unlock_irqrestore(&uartport->lock, flags);
        //SPSpinLock_ReleaseFromDpcLevel(&this_port_device->sSPSpinLock_candev, &flags);
    }
    
    //goto funcexit;
//funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    //DrvDbgPrint(0, "EvtInterruptDpc Exit \n");
    
    //return ntstatus;
}


/* Register the parallel port(s) of a PCI card. */
int
parport_register(
            struct pci_dev              *pcidev,
            const struct pci_device_id  *this_device_id
    )
{
    int                         retval;
    unsigned int                i;
    
    
    struct pci_driver_data      *this_driver_data;
    struct pci_board            *this_board;
    
    this_driver_data = pci_get_drvdata(pcidev);
    this_board = this_driver_data->this_board;
    
    retval = 0;
    
    if (this_board->num_parports == 1)
    {
        struct parport *port;
        unsigned long io_lo, io_hi;
        
        //because parport_pc_probe_port will do request_region, so pci_default_setup dont do request_region.
        //io_lo = (unsigned long)this_driver_data->MappedBaseAddress[this_board->BaseBarForParport_io];
        //io_hi = (unsigned long)this_driver_data->MappedBaseAddress[this_board->BaseBarForParport_iohi];
        io_lo = (unsigned long)pci_resource_start(pcidev, this_board->BaseBarForParport_io);
        io_hi = (unsigned long)pci_resource_start(pcidev, this_board->BaseBarForParport_iohi);
        
        DrvDbgPrint(0, "    PCI parallel port detected: %04x:%04x, I/O lo:%#lx hi:%#lx\n",
            this_board->vendor, this_board->device, io_lo, io_hi);
        
        port = my_parport_pc_probe_port(
            io_lo,
            io_hi,
            PARPORT_IRQ_NONE,
            PARPORT_DMA_NONE,
            &pcidev->dev
            );
        if (port)
        {
            DrvDbgPrint(0, "    parport_pc_probe_port success\n");
            this_driver_data->hparport[0] = port;
        }
        else
        {
            DrvDbgPrint(0, "    parport_pc_probe_port failed\n");
        }
    }
    
    return retval;
}


/* Register the serial port(s) of a PCI card. */
int
serial_register(
            struct pci_dev              *pcidev,
            const struct pci_device_id  *this_device_id
    )
{
    int                         retval;
                                // return -ERRNO
    
    //int                         reterr;
                                // check if (reterr < 0)
    
    unsigned int                result;
                                // check if (result != 0)
                                
    unsigned int                i;
    int                         minor;
    
    unsigned int                BusNumber;
    unsigned int                DeviceNumber;
    unsigned int                FunctionNumber;
    
    struct pci_driver_data      *this_driver_data;
    struct pci_board            *this_board;
    
    unsigned int                ClockRate;
    unsigned int                ClockDIV;
    unsigned int                MinBaudRateDivisor;
    PVOID                       MappedAddress_uart;
    PVOID                       MappedAddress_DMA;
    unsigned int                DMAEnable;
    
    struct uart_port            this_uart_port;     // as a temp variable.
    unsigned long               BaseAddress_start;
    int                         offset;
    
    unsigned int                bRegister;
    
    this_driver_data = pci_get_drvdata(pcidev);
    this_board = this_driver_data->this_board;
    
    retval = 0;
    
    BusNumber = pcidev->bus->number;
    DeviceNumber = PCI_SLOT(pcidev->devfn);
    FunctionNumber = PCI_FUNC(pcidev->devfn);
    
    if (this_board->PID == 0xA9610)
    {
        switch (mp_UARTSpeedMode)
        {
            case 1:
            {
                ClockRate = 24000000;
                ClockDIV = 16;
                MinBaudRateDivisor = 1;
            }
            break;
            
            case 4:
            {
                ClockRate = 1846153;
                ClockDIV = 8;
                MinBaudRateDivisor = 1;
            }
            break;
            
            case 5:
            {
                ClockRate = 24000000;
                ClockDIV = 13;
                MinBaudRateDivisor = 1;
            }
            break;
            
            case 6:
            {
                ClockRate = 50000000;
                ClockDIV = 1;
                MinBaudRateDivisor = 5;
            }
            break;
            
            default:
            {
                ClockRate = 1846153;
                ClockDIV = 16;
                MinBaudRateDivisor = 1;
            }
            break;
            
        }
    }
    else
    {
        ClockRate = 14745600;
        ClockDIV = 16;
        MinBaudRateDivisor = 1;
    }
    
    DrvDbgPrint(0, "    create(or assign) uart port device for each port \n");
    // create uart port device for each port.
    for (i = 0; i < this_board->num_ports; i++)
    {
        // here i as port id of this multiple serial device.
        DrvDbgPrint(0, "    port id: %u \n", i);
        
        // init_uart_port(this_board, this_driver_data, i, this_uart_port);
        {
            memset(&this_uart_port, 0, sizeof(struct uart_port));
            this_uart_port.flags = UPF_SKIP_TEST | UPF_BOOT_AUTOCONF | UPF_SHARE_IRQ;
            this_uart_port.uartclk = ClockRate; //ClockRate = SourceClock = this_board->base_baud * ClockDIV(16);
            this_uart_port.irq = pcidev->irq;
            this_uart_port.dev = &pcidev->dev;    //parent device
            
            BaseAddress_start = pci_resource_start(pcidev, this_board->BaseBarForUart);
            offset = this_board->first_offset + i*this_board->uart_offset; // start offset + port index * uart io length.
            if (pci_resource_flags(pcidev, this_board->BaseBarForUart) & IORESOURCE_MEM)
            {
                this_uart_port.iotype = UPIO_MEM;
                this_uart_port.iobase = 0;
                this_uart_port.mapbase = BaseAddress_start + offset;
                this_uart_port.membase = this_driver_data->MappedBaseAddress[this_board->BaseBarForUart] + offset;
                this_uart_port.regshift = this_board->reg_shift;
            }
            else
            {
                this_uart_port.iotype = UPIO_PORT;
                this_uart_port.iobase = BaseAddress_start + offset;
                this_uart_port.mapbase = 0;
                this_uart_port.membase = NULL;
                this_uart_port.regshift = 0;
            }
            
            //this_uart_port.serial_in = uart_serial_in;
            //this_uart_port.serial_out = uart_serial_out;
            
            DrvDbgPrint(0, "    this_uart_port.iobase:0x%x \n", (unsigned int)this_uart_port.iobase);
            DrvDbgPrint(0, "    this_uart_port.mapbase:0x%x \n", (unsigned int)this_uart_port.mapbase);
        }
        
        if (this_board->PID == 0xA9610)
        {
            MappedAddress_uart = this_driver_data->MappedBaseAddress[this_board->BaseBarForUart] + offset;
            MappedAddress_DMA = this_driver_data->MappedBaseAddress[this_board->BaseBarForDMA] + 0x20*i;
            DMAEnable = mp_DMAEnable;
        }
        else
        {
            MappedAddress_uart = this_driver_data->MappedBaseAddress[this_board->BaseBarForUart] + offset;
            MappedAddress_DMA = NULL;
            DMAEnable = FALSE;
        }
        
        minor = 0;
        bRegister = FALSE;
        //result = register_uart_port(this_uart_port, &uart_port_minor);
        DrvDbgPrint(0, "    register_uart_port \n");
        {
            mutex_lock(&ThisModule_uart_mutex);
            
            // find a non used uart port device and assign(attach) it to this port.
            for (minor = 0; minor < NR_PORTS; minor++)
            {
                if (ThisModule_uart_port_devices[minor].pcidev == NULL)
                {
                    ThisModule_uart_port_devices[minor].pcidev = pcidev;
                    ThisModule_uart_port_devices[minor].port = this_uart_port;
                    
                    ThisModule_uart_port_devices[minor].sMappedRegisters_uart.AddressSpace = 1;
                    ThisModule_uart_port_devices[minor].sMappedRegisters_uart.MappedAddress = MappedAddress_uart;
                    
                    ThisModule_uart_port_devices[minor].sMappedRegisters_DMA.AddressSpace = 0;
                    ThisModule_uart_port_devices[minor].sMappedRegisters_DMA.MappedAddress = MappedAddress_DMA;
                    
                    ThisModule_uart_port_devices[minor].ClockRate = ClockRate;
                    ThisModule_uart_port_devices[minor].ClockDIV = ClockDIV;
                    ThisModule_uart_port_devices[minor].MinBaudRateDivisor = MinBaudRateDivisor;
                    ThisModule_uart_port_devices[minor].DMAEnable = DMAEnable;
                    
                    tasklet_init(&ThisModule_uart_port_devices[minor].ISRDPCTasklet, ISRDPCRoutine, (UINTPTR)minor);
                    spin_lock_init(&ThisModule_uart_port_devices[minor].spinlock_ISRDPCQueue);
                    SPZeroMemory(&ThisModule_uart_port_devices[minor].sInterruptExtension, sizeof(INTERRUPT_EXTENSION));
                    
                    ThisModule_uart_port_devices[minor].hHwdEUART = &ThisModule_uart_port_devices[minor].sHwdEUART;
                    
                    HwdEUART_Initialize(&ThisModule_uart_port_devices[minor].sHwdEUART);
                    HwdEUART_SetMappedRegisters(
                        &ThisModule_uart_port_devices[minor].sHwdEUART,
                        &ThisModule_uart_port_devices[minor].sMappedRegisters_uart,
                        &ThisModule_uart_port_devices[minor].sMappedRegisters_DMA
                        );
                    
                    result = HwdEUART_OpenDevice(
                        &ThisModule_uart_port_devices[minor].sHwdEUART,
                        pcidev,
                        BusNumber,
                        DeviceNumber,
                        FunctionNumber,
                        i // portid
                        );
                    if (!FR_SUCCESS(result))
                    {
                        DrvDbgPrint(0, "    HwdEUART_OpenDevice Error:0x%x \n", result);
                        bRegister = FALSE;
                        ThisModule_uart_port_devices[minor].pcidev = NULL;
                        break;
                    }
                    
                    ThisModule_uart_port_devices[minor].portid = i;
                    //ThisModule_uart_port_devices[minor].shared_irq_entry;
                        // we will link shared_irq_entry on uart_ops.setup().
                    
                    ThisModule_uart_port_devices[minor].port.line = minor;
                    spin_lock_init(&ThisModule_uart_port_devices[minor].port.lock);
                    
                    // no irq serial port may use a timer.
                        // our device should have a irq, so we dont support a timer for no irq handle.
                    //init_timer(&ThisModule_uart_port_devices[minor].timer);
                    //ThisModule_uart_port_devices[minor].timer.function = serialxr_timeout;
                    
                    ThisModule_uart_port_devices[minor].port.ops = &ThisModule_uart_ops;
                    
                    // Possibly override default I/O functions.
                    //ThisModule_uart_port_devices[minor].port.serial_in = this_uart_port.serial_in;
                    //ThisModule_uart_port_devices[minor].port.serial_out = this_uart_port.serial_out;
                    
                    ThisModule_uart_port_devices[minor].uartconfigtype = this_board->uartconfigtype;
                    
                    result = uart_add_one_port(&ThisModule_uart_driver, &ThisModule_uart_port_devices[minor].port);
                    //in uart_add_one_port, serial core call uart_ops_config_port() and uart_ops_type().
                    if (result != 0)
                    {
                        DrvDbgPrint(0, "    unable to register port with minor %i \n", minor);
                    }
                    else
                    {
                        DrvDbgPrint(0, "    now this port is registered with minor %i \n", minor);
                        bRegister = TRUE;
                    }
                    break;
                }
            }
            
            mutex_unlock(&ThisModule_uart_mutex);
            if (minor == NR_PORTS)
            {
                DrvDbgPrint(0, "    there is no non-used minor port\n");
            }
        }
        
        if (bRegister == TRUE)
        {
            this_driver_data->uart_port_minor[i] = minor;
        }
        else
        {
            this_driver_data->uart_port_minor[i] = not_assign;
        }
    }
    
    return retval;
}


/**
 *    Register PCI device with kernel services
 *    @pdev: PCI device to register
 *    @pid: Entry in sch_pci_tbl matching with @pdev
 *
 *    LOCKING:
 *    Inherited from PCI layer (may sleep).
 *
 *    RETURNS:
 *    Zero on success, or -ERRNO value.
 */
int
pcidevice_probe(
            struct pci_dev *                pcidev, // this_pci_dev
            const struct pci_device_id *    this_device_id
    )
{
    int                         retval;
                                // return -ERRNO
    
    //int                         reterr;
                                // check if (reterr < 0)
    
    unsigned int                result;
                                // check if (result != 0)
    
    unsigned int                i;
    
    struct pci_board            *this_board;
    struct pci_driver_data      *this_driver_data;
    
    DrvDbgPrint(0, "pcidevice_probe Entry \n");
    
    //
    // initialize local variables
    //
    
    retval = 0;
    this_board = NULL;
    this_driver_data = NULL;
    
    //
    // check parameters
    //
    
    
    //
    // function body
    //
    
    // check if we support this device, then get its specific information.
    DrvDbgPrint(0, "    find and check this pci specific, VendorID:0x%x DeviceID:0x%x ID:%u \n",
        this_device_id->vendor, this_device_id->device, this_device_id->driver_data);
    
    // check if we support this device and find this_board information.
    if (this_device_id->driver_data >= ARRAY_SIZE(ThisModule_pci_boards))  // use driver_data as board number(index).
    {
        DrvDbgPrint(0, "    invalid device \n");
        retval = -EINVAL;
        goto funcexit;
    }
    
    this_board = &ThisModule_pci_boards[this_device_id->driver_data];
    
    // if this device is supported by this module,
    // we create releated device data for it and create uart ports of this device for ThisModule_uart_driver
    
    // create driver data
    this_driver_data = kzalloc(sizeof(struct pci_driver_data), GFP_ATOMIC);
    if (this_driver_data == NULL)
    {
        DrvDbgPrint(0, "    this_driver_data == NULL \n");
        retval = -ENOMEM;
        goto funcexit;
    }
    
    // init driver data
    this_driver_data->pcidev = pcidev;
    this_driver_data->this_board = this_board;
    for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++)
    {
        this_driver_data->hresource[i] = NULL;
        this_driver_data->BaseAddressSpace[i] = 0;
        this_driver_data->BaseAddress[i] = 0;
        this_driver_data->BaseAddressLength[i] = 0;
        this_driver_data->MappedBaseAddress[i] = NULL;
    }
    
    for (i = 0; i < max_uart_port_minor; i++)
    {
        this_driver_data->uart_port_minor[i] = not_assign;
    }
    
    for (i = 0; i < max_parport; i++)
    {
        this_driver_data->hparport[i] = NULL;
    }
    
    // pci device init & setup
    DrvDbgPrint(0, "    pci device init() \n");
    if (this_board->init)
    {
        this_board->init(pcidev, this_driver_data);
    }
    
    DrvDbgPrint(0, "    pci device setup() \n");
    if (this_board->setup)
    {
        result = this_board->setup(pcidev, this_driver_data, IRQHandler);
        if (result != 0)
        {
            retval = -EINVAL;
            goto funcexit;
        }
    }
    
    if (parport_register(pcidev, this_device_id))
    {
        retval = -ENODEV;
        goto funcexit;
    }
    
    if (serial_register(pcidev, this_device_id))
    {
        retval = -ENODEV;
        goto funcexit;
    }
    
    
    // initialize controller
    
    // start device
    
    // enable interrupt
    
    DrvDbgPrint(0, "    pcidevice_probe Ok \n");
    
    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    if (retval < 0)    // error occur
    {
        if (this_driver_data != NULL)
        {   
            kfree(this_driver_data);
            this_driver_data = NULL;
        }
    }
    
    return retval;
}


void
pcidevice_remove(
            struct pci_dev *                pcidev  // this_pci_dev
    )
{
    unsigned int                i;
    int                         minor;
    
    struct pci_driver_data      *this_driver_data;
    struct pci_board            *this_board;
    
    DrvDbgPrint(0, "pcidevice_remove Entry \n");
    
    //
    // initialize local variables
    //
    
    this_driver_data = NULL;
    
    //
    // check parameters
    //
    
    
    //
    // function body
    //
    
    this_driver_data = pci_get_drvdata(pcidev);
    this_board = this_driver_data->this_board;
    
    // disable interrupt
    
    // stop device
    for (i = 0; i < max_parport; i++)
    {
        if (this_driver_data->hparport[i] != NULL)
        {
            my_parport_pc_unregister_port(this_driver_data->hparport[i]);
            this_driver_data->hparport[i] = NULL;
        }
    }
    
    DrvDbgPrint(0, "    delete(or unassign) uart port device for each port \n");
    // delete uart port device for each port.
    for (i = 0; i < this_board->num_ports; i++)
    {
        DrvDbgPrint(0, "    port id: %u \n", i);
        
        minor = this_driver_data->uart_port_minor[i];
        
        if (minor == not_assign)
        {
            DrvDbgPrint(0, "    this port have not been assigned to a minor port \n");
        }
        else
        {
            tasklet_kill(&ThisModule_uart_port_devices[minor].ISRDPCTasklet);
            
            HwdEUART_CloseDevice(
                &ThisModule_uart_port_devices[minor].sHwdEUART
                );
            
            // unregister_uart_port
            DrvDbgPrint(0, "    unregister_uart_port this port \n");
            
            mutex_lock(&ThisModule_uart_mutex);
                
            uart_remove_one_port(&ThisModule_uart_driver, &ThisModule_uart_port_devices[minor].port);
            ThisModule_uart_port_devices[minor].pcidev = NULL;
                
            mutex_unlock(&ThisModule_uart_mutex);
        }
        
        this_driver_data->uart_port_minor[i] = not_assign;
    }
    
    // pci device exit
    DrvDbgPrint(0, "    pci device exit() \n");
    if (this_board->exit)
    {
        this_board->exit(pcidev, this_driver_data);
    }
    
    // free driver data
    kfree(this_driver_data);
    
    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    //return retval;
    return;
}

int
pcidevice_suspend(
            struct pci_dev *                pcidev, // this_pci_dev
            pm_message_t                    state
    )
{
    int                         retval;
                                // return -ERRNO
    
    //int                         reterr;
                                // check if (reterr < 0)
    
    //unsigned int                result;
                                // check if (result != 0)
                                
    unsigned int                i;
    int                         moduleportindex;
    
    
    struct pci_driver_data      *this_driver_data;
    struct pci_board            *this_board;
    
    //
    // initialize local variables
    //
    
    this_driver_data = NULL;
    
    //
    // check parameters
    //
    
    
    //
    // function body
    //
    
    this_driver_data = pci_get_drvdata(pcidev);
    
    this_board = this_driver_data->this_board;
    
    DrvDbgPrint(0, "pcidevice_suspend Entry \n");
    DrvDbgPrint(0, "    pm_state:D%i \n", state);
    
    // suspend each port device
    for (i = 0; i < this_board->num_ports; i++)
    {
        //DrvDbgPrint(0, "port id: %u \n", i);
        
        moduleportindex = this_driver_data->uart_port_minor[i];
        
        if (moduleportindex == not_assign)
        {
            //DrvDbgPrint(0, "this port have not been assigned to a module port \n");
        }
        else
        {
            //module_suspend_candev(moduleportindex);
        }
    }
    
    // suspend pci device
    pci_enable_wake(pcidev, PCI_D3hot, 0);  // disable d3 wakeup
    
    retval = pci_save_state(pcidev);
    if (retval < 0)
    {
        DrvDbgPrint(0, "    pci_save_state failed.\n");
    }
    else
    {
        pci_clear_master(pcidev);
           
        pci_disable_device(pcidev);
        
        pci_set_power_state(pcidev, pci_choose_state(pcidev, state));
    }
    
    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    return retval;
    //return;
}
    

int
pcidevice_resume(
            struct pci_dev *                pcidev  // this_pci_dev
    )
{
    int                         retval;
                                // return -ERRNO
    
    //int                         reterr;
                                // check if (reterr < 0)
    
    //unsigned int                result;
                                // check if (result != 0)
                                
    unsigned int                i;
    int                         moduleportindex;
    
    struct pci_driver_data      *this_driver_data;
    struct pci_board            *this_board;
    
    //
    // initialize local variables
    //
    
    this_driver_data = NULL;
    
    //
    // check parameters
    //
    
    
    //
    // function body
    //
    
    this_driver_data = pci_get_drvdata(pcidev);
    
    this_board = this_driver_data->this_board;
    
    DrvDbgPrint(0, "pcidevice_resume Entry \n");
    
    // resume pci device
    pci_enable_wake(pcidev, PCI_D3hot, 0);  // disable d3 wakeup
    
    pci_restore_state(pcidev);
    
    retval = pci_enable_device(pcidev);
    if (retval < 0)
    {
        DrvDbgPrint(0, "    pci_enable_device failed.\n");
        return retval;
    }
    
    pci_set_power_state(pcidev, PCI_D0);
    
    
    // resume each port device
    for (i = 0; i < this_board->num_ports; i++)
    {
        //DrvDbgPrint(0, "port id: %u \n", i);
        
        moduleportindex = this_driver_data->uart_port_minor[i];
        
        if (moduleportindex == not_assign)
        {
            //DrvDbgPrint(0, "this port have not been assigned to a minor port \n");
        }
        else
        {
            //module_resume_candev(moduleportindex);
        }
    }
    
    if (this_board->PID == 0xA9610)
    {
        pci_set_master(pcidev);
    }
    
    
    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    return retval;
    //return;
}


int
pci_default_init(
    struct pci_dev *            pcidev,
    struct pci_driver_data *    this_driver_data
    )
{
    int                         retval;
                                // return -ERRNO
    
    //int                         reterr;
                                // check if (reterr < 0)
    
    unsigned int                result;
                                // check if (result != 0)
    
    unsigned int                i;
    
    unsigned int                BusNumber;
    unsigned int                DeviceNumber;
    unsigned int                FunctionNumber;
    
    struct pci_board            *this_board;
    
    unsigned int                Port422485;
    
    //
    // initialize local variables
    //
    
    retval = 0;
    
    //
    // check parameters
    //
    
    
    //
    // function body
    //
    
    this_board = this_driver_data->this_board;
    
    // assign Driver Data to pci device
    pci_set_drvdata(pcidev, this_driver_data);
    
    /* enable device and prepare host */
    result = pci_enable_device(pcidev);
    if (result != 0)
    {
        DrvDbgPrint(0, "    pci_enable_device failed \n");
        retval = result;
        goto funcexit;
    }
    
    // after pci_enable_device, pcidev->irq become routed irq.
    this_driver_data->irq = pcidev->irq;
    
    // pci_set_master(pcidev);
        // our serial pci card dont support bus master.
    
    // get pci bus information.
    BusNumber = pcidev->bus->number;
    DeviceNumber = PCI_SLOT(pcidev->devfn);
    FunctionNumber = PCI_FUNC(pcidev->devfn);
    
    // show some of pci configuration.
    DrvDbgPrint(0, "    BusNumber:%u DeviceNumber:%u FunctionNumber:%u \n",
        BusNumber, DeviceNumber, FunctionNumber);
    
    result = 0;
    ReadPCIConfiguration(
        BusNumber,
        DeviceNumber,
        FunctionNumber,
        4,
        2, // 1 , 2 , 4
        &result
        );
    DrvDbgPrint(0, "    Command Register:0x%x \n", result);
    
    result = 0;
    ReadPCIConfiguration(
        BusNumber,
        DeviceNumber,
        FunctionNumber,
        6,
        2, // 1 , 2 , 4
        &result
        );
    DrvDbgPrint(0, "    Status Register:0x%x \n", result);
    
    result = 0;
    ReadPCIConfiguration(
        BusNumber,
        DeviceNumber,
        FunctionNumber,
        0x10,
        4, // 1 , 2 , 4
        &result
        );
    DrvDbgPrint(0, "    Base0 Register:0x%x \n", result);
    
    result = 0;
    ReadPCIConfiguration(
        BusNumber,
        DeviceNumber,
        FunctionNumber,
        0x14,
        4, // 1 , 2 , 4
        &result
        );
    DrvDbgPrint(0, "    Base1 Register:0x%x \n", result);
    
    result = 0;
    ReadPCIConfiguration(
        BusNumber,
        DeviceNumber,
        FunctionNumber,
        0x18,
        4, // 1 , 2 , 4
        &result
        );
    DrvDbgPrint(0, "    Base2 Register:0x%x \n", result);
    
    result = 0;
    ReadPCIConfiguration(
        BusNumber,
        DeviceNumber,
        FunctionNumber,
        0x1C,
        4, // 1 , 2 , 4
        &result
        );
    DrvDbgPrint(0, "    Base3 Register:0x%x \n", result);
    
    // initialize this pci device through pci configure.
    if (this_board->vendor == 0x1bbc)
    {
        if (this_board->uartconfigtype == EUART_422485_V1)
        {
            if (mp_422mode != 0)
            {
                Port422485 = 0xFF;
                
                WritePCIConfiguration(
                    BusNumber,
                    DeviceNumber,
                    FunctionNumber,
                    0x42,
                    1,
                    &Port422485
                    );
                
                DrvDbgPrint(0, "set to 422mode \n");
            }
            else
            {
                Port422485 = 0x00;
                
                WritePCIConfiguration(
                    BusNumber,
                    DeviceNumber,
                    FunctionNumber,
                    0x42,
                    1,
                    &Port422485
                    );
                
                DrvDbgPrint(0, "set to 485mode \n");
            }
        }
    }
    if (this_board->PID == 0xA9610)
    {
        pci_set_master(pcidev);
        for (i = 0; i < this_board->num_ports; i++)
        {
            PCIConfigure_A9610(
                BusNumber,
                DeviceNumber, 
                FunctionNumber,
                i,
                mp_UARTSpeedMode
                );
        }
    }
    
    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    return retval;
}


int
pci_default_setup(
    struct pci_dev *            pcidev,
    struct pci_driver_data *    this_driver_data,
    irq_handler_t               handler
    )
{
    int                         retval;
                                // return -ERRNO
    
    //int                         reterr;
                                // check if (reterr < 0)
    
    //unsigned int                result;
                                // check if (result != 0)
    
    unsigned int                i;
    
    struct pci_board            *this_board;
    
    unsigned int                BaseAddress_space;
    unsigned long               BaseAddress_start;
    unsigned long               BaseAddress_length;
    
    struct resource             *hresource;
    void                        *MappedAddress;
    
    struct shared_irq_list      *this_shared_irq_list;
    
    //
    // initialize local variables
    //
    
    retval = 0;
    
    //
    // check parameters
    //
    
    
    //
    // function body
    //
    
    this_board = this_driver_data->this_board;
    
    // allocate io resoures
    DrvDbgPrint(0, "    allocating io resources\n");
    for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++)
    {
        BaseAddress_start = pci_resource_start(pcidev, i);
        BaseAddress_length = pci_resource_len(pcidev, i);
        
        /*
        // we just allocate the right io size, because other io is reserved for GPIO.
        if (this_board->num_ports*this_board->uart_offset > BaseAddress_length)
        {
            DrvDbgPrint(0, "num_ports *  uart_offset > BaseAddress_length \n");
            retval = -EFAULT;
            goto funcexit;
        }
        BaseAddress_length = this_board->num_ports*this_board->uart_offset;
        */
        
        BaseAddress_space = 1;
        if (pci_resource_flags(pcidev, i) & IORESOURCE_MEM)
        {
            BaseAddress_space = 0;
        }
        
        if ( (i == this_board->BaseBarForUart || i == this_board->BaseBarForDMA) &&
            BaseAddress_length != 0)
        {
            if (BaseAddress_space == 0)
            {   // memory space
                //struct resource *request_mem_region(unsigned long start, unsigned long len, char *name);
                hresource = request_mem_region(
                    BaseAddress_start,
                    BaseAddress_length,
                    NULL
                    );
                if (hresource == NULL)
                {
                    DrvDbgPrint(0, "    request_mem_region hresource == NULL \n");
                    retval = -EFAULT;
                    //goto funcexit;
                }
                
                // void *ioremap_nocache(unsigned long phys_addr, unsigned long size);
                MappedAddress = ioremap_nocache(
                    BaseAddress_start,
                    BaseAddress_length
                    );
                if (MappedAddress == NULL)
                {
                    DrvDbgPrint(0, "    ioremap_nocache MappedAddress == NULL \n");
                    retval = -EFAULT;
                    //goto funcexit;
                }
            }
            else
            {   // io space
                //struct resource *request_region(unsigned long start, unsigned long len, char *name);
                hresource = request_region(
                    BaseAddress_start,
                    BaseAddress_length,
                    NULL
                    );
                if (hresource == NULL)
                {
                    DrvDbgPrint(0, "    request_region hresource == NULL \n");
                    retval = -EFAULT;
                    //goto funcexit;
                }
                
                MappedAddress = (void*)(UINTPTR)BaseAddress_start;
            }
            
            this_driver_data->hresource[i] = hresource;
            this_driver_data->BaseAddressSpace[i] = BaseAddress_space;
            this_driver_data->BaseAddress[i] = BaseAddress_start;
            this_driver_data->BaseAddressLength[i] = BaseAddress_length;
            this_driver_data->MappedBaseAddress[i] = MappedAddress;
            
            if (retval < 0)
            {
                goto funcexit;
            }
            
            DrvDbgPrint(0, "    BaseAddress[%u] \n", i);
            DrvDbgPrint(0, "    BaseAddressSpace:0x%x \n", BaseAddress_space);
            DrvDbgPrint(0, "    BaseAddress:0x%lx \n", BaseAddress_start);
            DrvDbgPrint(0, "    BaseAddressLength:0x%lx \n", BaseAddress_length);
            DrvDbgPrint(0, "    MappedBaseAddress:0x%p \n", MappedAddress);
            
        }
    }
    
    // request irq
    // install IRQ handle
    
    // if this pci device provide one controller, we can use this_driver_data as a medium data for irq handle.
    // if this pci device provide more than one controller, we should use this_shared_irq_list to link all controllers
    //      and use it as as a medium data for irq handle.
    
    /* 
    this_driver_data->bRequestIRQ = FALSE;
    if (request_irq(pcidev->irq, handler, IRQF_SHARED, DRIVER_NAME, this_driver_data) != 0)
    {
        DrvDbgPrint(0, "request interrupt %i failed\n", pcidev->irq);
        retval = -EBUSY;
        goto done;
    }
    this_driver_data->bRequestIRQ = TRUE;
    */
    
    if (is_real_interrupt(pcidev->irq))
    {
        this_shared_irq_list = &ThisModule_shared_irq_lists[pcidev->irq];
        
        spin_lock_irq(&this_shared_irq_list->lock);
        
        if (this_shared_irq_list->referencecount == 0)
        {
            this_shared_irq_list->referencecount = 1;
            
            spin_unlock_irq(&this_shared_irq_list->lock);
            
            // PS: request_irq is sleeping function. it cannot be call in irq lock.
            // register irq handler
            if (request_irq(pcidev->irq, handler, IRQF_SHARED, DRIVER_NAME, this_shared_irq_list) != 0)
            {
                DrvDbgPrint(0, "    request interrupt %i failed\n", pcidev->irq);
                retval = -EFAULT;
                goto funcexit;
            }
            else
            {
                DrvDbgPrint(0, "    request IRQ %i handler success \n", pcidev->irq);
            }
        }
        else
        {
            this_shared_irq_list->referencecount += 1;
            
            spin_unlock_irq(&this_shared_irq_list->lock);
            
            DrvDbgPrint(0, "    this_shared_irq_list->referencecount += 1; \n");
        }
    }
    else
    {
        DrvDbgPrint(0, "    No IRQ.  Check PCI setup!\n");
    }
    
    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    if (retval < 0)    // error occur
    {
        for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++)
        {
            if (i == this_board->BaseBarForUart || i == this_board->BaseBarForDMA )
            {
                if (this_driver_data->hresource[i] != NULL)
                {
                    if (this_driver_data->BaseAddressSpace[i] == 0)
                    {
                        // void iounmap(void * addr);
                        if (this_driver_data->MappedBaseAddress[i] != NULL)
                        {
                            iounmap(this_driver_data->MappedBaseAddress[i]);
                        }
                        
                        // void release_mem_region(unsigned long start, unsigned long len);
                        release_mem_region(
                            this_driver_data->BaseAddress[i],
                            this_driver_data->BaseAddressLength[i]
                            );
                    }
                    else
                    {
                        // void release_region(unsigned long start, unsigned long len);
                        release_region(
                            this_driver_data->BaseAddress[i],
                            this_driver_data->BaseAddressLength[i]
                            );
                    }
                    
                    this_driver_data->hresource[i] = NULL;
                }
            }
        }
    }
    
    
    return retval;
}


void
pci_default_exit(
    struct pci_dev *            pcidev,
    struct pci_driver_data *    this_driver_data
    )
{
    unsigned int                i;
    
    struct pci_board            *this_board;
    
    struct shared_irq_list      *this_shared_irq_list;
    
    //
    // initialize local variables
    //
    
    
    //
    // check parameters
    //
    
    
    //
    // function body
    //
    
    this_board = this_driver_data->this_board;
    
    // release irq
    
    if (is_real_interrupt(pcidev->irq))
    {
        this_shared_irq_list = &ThisModule_shared_irq_lists[pcidev->irq];
        
        spin_lock_irq(&this_shared_irq_list->lock);
        
        this_shared_irq_list->referencecount -= 1;
        
        if (this_shared_irq_list->referencecount == 0)
        {
            // PS: free_irq is sleeping function. it cannot be call in irq lock.
            spin_unlock_irq(&this_shared_irq_list->lock);
            
            free_irq(pcidev->irq, this_shared_irq_list);
            DrvDbgPrint(0, "    free_irq %i \n", pcidev->irq);
        }
        else
        {
            spin_unlock_irq(&this_shared_irq_list->lock);
        }
    }
    
    // free io resoureces
    for (i = 0; i < PCI_NUM_BAR_RESOURCES; i++)
    {
        if (i == this_board->BaseBarForUart || i == this_board->BaseBarForDMA )
        {
            if (this_driver_data->hresource[i] != NULL)
            {
                if (this_driver_data->BaseAddressSpace[i] == 0)
                {
                    // void iounmap(void * addr);
                    if (this_driver_data->MappedBaseAddress[i] != NULL)
                    {
                        iounmap(this_driver_data->MappedBaseAddress[i]);
                    }
                    
                    // void release_mem_region(unsigned long start, unsigned long len);
                    release_mem_region(
                        this_driver_data->BaseAddress[i],
                        this_driver_data->BaseAddressLength[i]
                        );
                }
                else
                {
                    // void release_region(unsigned long start, unsigned long len);
                    release_region(
                        this_driver_data->BaseAddress[i],
                        this_driver_data->BaseAddressLength[i]
                        );
                }
                
                this_driver_data->hresource[i] = NULL;
            }
        }
    }
    
    pci_clear_master(pcidev);
    
    pci_disable_device(pcidev);
    
    pci_set_drvdata(pcidev, NULL);
    
    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    //return retval;
    return;
}

unsigned int
uart_serial_in(
    struct uart_port *          uartport,
    int                         offset
    )
{
    offset <<= uartport->regshift;
    
    switch (uartport->iotype)
    {
        case SERIAL_IO_MEM:
        {
            return readb(uartport->membase + offset);
        }
    
        default:
        {
            return inb(uartport->iobase + offset);
        }
    }
}


void
uart_serial_out(
    struct uart_port *          uartport,
    int                         offset,
    int                         value
    )
{
    offset <<= uartport->regshift;
    
    switch (uartport->iotype)
    {
        case SERIAL_IO_MEM:
        {
            writeb(value, uartport->membase + offset);
        }
        break;
    
        default:
        {
            outb(value, uartport->iobase + offset);
        }
        break;
    }
}


int
uart_ops_request_port(
    struct uart_port *          uartport
    )
{
    // Request any memory and IO region resources required by the port
    // we has done this at the process of pci_default_setup and pcidevice_probe
    
    struct uart_port_device     *uartportdevice;
    
    uartportdevice = (struct uart_port_device *)uartport;
    
    //DrvDbgPrint(0, "uart_ops_request_port: portid:%u minor:%i \n", uartportdevice->portid, uartportdevice->minor);
    
    return 0;
}


void
uart_ops_release_port(
    struct uart_port *          uartport
    )
{    
    //Release any memory and IO region resources currently in use by the port.
    
    struct uart_port_device     *uartportdevice;
    
    uartportdevice = (struct uart_port_device *)uartport;
    
    //DrvDbgPrint(0, "uart_ops_release_port: portid:%u minor:%i \n", uartportdevice->portid, uartportdevice->minor);
}


void
uart_ops_config_port(
    struct uart_port *          uartport,
    int                         flags
    )
{
    struct uart_port_device     *uartportdevice;
    struct pci_driver_data      *this_driver_data;
    struct pci_board            *this_board;
    
    DrvDbgPrint(0, "uart_ops_config_port Entry \n");
    
    //
    // initialize local variables
    //
    
    //
    // check parameters
    //
    
    
    //
    // function body
    //
    
    uartportdevice = (struct uart_port_device *)uartport;
    this_driver_data = pci_get_drvdata(uartportdevice->pcidev);
    this_board = this_driver_data->this_board;
    
    DrvDbgPrint(0, "    uart_ops_config_port: portid:%u minor:%i \n", uartportdevice->portid, uartportdevice->minor);
    
    if (flags & UART_CONFIG_TYPE)
    {
        if (this_board->PID == 0xA9610)
        {
            uartport->type = uartportdevice->uartconfigtype;
            if (uartportdevice->DMAEnable == TRUE)
            {
                uartportdevice->tx_loadsz = 64;
                uartport->fifosize = 128;
            }
            else
            {
                uartportdevice->tx_loadsz = ThisModule_uart_config[uartportdevice->uartconfigtype].tx_loadsz;
                uartport->fifosize = ThisModule_uart_config[uartportdevice->uartconfigtype].fifo_size;
            }
            //uartportdevice->capabilities = ThisModule_uart_config[uartportdevice->uartconfigtype].capabilities;
            
            // reset DMA and disable DMA
            HwdEUART_HardResetDMA(
                uartportdevice->hHwdEUART
                );
            //HwdEUART_InitDMA(
            //    uartportdevice->hHwdEUART
            //    );
            
            // reset the UART.
            // uart_ops_startup will do this.
            uart_serial_out(uartport, UART_MCR, 0);
            uart_serial_out(uartport, UART_LCR, 0);
            uart_serial_out(uartport, UART_IER, 0);
            uart_serial_out(uartport, UART_FCR, 0);
            // clear interrupt.
            uart_serial_in(uartport, UART_LSR);
            uart_serial_in(uartport, UART_RX);
            uart_serial_in(uartport, UART_IIR);
            uart_serial_in(uartport, UART_MSR);
        }
        else
        {
            uartport->type = uartportdevice->uartconfigtype;
            uartportdevice->tx_loadsz = ThisModule_uart_config[uartportdevice->uartconfigtype].tx_loadsz;
            uartport->fifosize = ThisModule_uart_config[uartportdevice->uartconfigtype].fifo_size;
            //uartportdevice->capabilities = ThisModule_uart_config[uartportdevice->uartconfigtype].capabilities;
            
            // reset the UART.
            // uart_ops_startup will do this.
            uart_serial_out(uartport, UART_MCR, 0);
            uart_serial_out(uartport, UART_LCR, 0);
            uart_serial_out(uartport, UART_IER, 0);
            uart_serial_out(uartport, UART_FCR, 0);
            // clear interrupt.
            uart_serial_in(uartport, UART_LSR);
            uart_serial_in(uartport, UART_RX);
            uart_serial_in(uartport, UART_IIR);
            uart_serial_in(uartport, UART_MSR);
            
        }
        
        DrvDbgPrint(0, "    uart_ops_config_port: UART_CONFIG_TYPE \n");
        DrvDbgPrint(0, "    uart_ops_config_port: uartconfigtype:%u \n", uartportdevice->uartconfigtype);
        DrvDbgPrint(0, "    uart_ops_config_port: tx_loadsz:%u \n", uartportdevice->tx_loadsz);
        DrvDbgPrint(0, "    uart_ops_config_port: fifosize:%u \n", uartport->fifosize);
        DrvDbgPrint(0, "    uart_ops_config_port: capabilities:0x%x \n", ThisModule_uart_config[uartportdevice->uartconfigtype].capabilities);
    }
    
    if (flags & UART_CONFIG_IRQ)
    {
        DrvDbgPrint(0, "    uart_ops_config_port: UART_CONFIG_IRQ \n");
        DrvDbgPrint(0, "    uart_ops_config_port: irq %i \n", uartport->irq);
        // we has done this at the process of pcidevice_probe
        
        //uartport->irq = pcidev->irq;
        
        // reset uart interrupt.
        // uart_ops_startup will do this.
    }
    
    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    DrvDbgPrint(0, "uart_ops_config_port Exit \n");
    
    //return retval;
    return;
}


const char *
uart_ops_type(
    struct uart_port *          uartport
    )
{
    struct uart_port_device     *uartportdevice;
    
    DrvDbgPrint(0, "uart_ops_type Entry \n");
    
    uartportdevice = (struct uart_port_device *)uartport;
    
    DrvDbgPrint(0, "    uart_ops_type: portid:%u minor:%i \n", uartportdevice->portid, uartportdevice->minor);
    
    DrvDbgPrint(0, "    uart_ops_type name:%s \n", ThisModule_uart_config[uartportdevice->uartconfigtype].name);
    
    // maybe, we can use this to tell the user this port belogs to which pci card.
    
    return ThisModule_uart_config[uartportdevice->uartconfigtype].name;
}


int
uart_ops_startup(
    struct uart_port *          uartport
    )
    // at startup, serial core think that ModemStatus Interrupt is disable.
    // after pcidevice_probe, OS kernel call this function.
{
    int                         retval;
    
    struct uart_port_device     *uartportdevice;
    struct pci_driver_data      *this_driver_data;
    struct pci_board            *this_board;
    
    unsigned long               flags;
                                //irq flags use unsigned long in linux
    
    unsigned char               lsr;
    //unsigned char               iir;
    
    struct shared_irq_list      *this_shared_irq_list;
    
    DrvDbgPrint(0, "uart_ops_startup Entry \n");
    
    //
    // initialize local variables
    //
    
    retval = 0;
    
    //
    // check parameters
    //
    
    
    //
    // function body
    //
    
    uartportdevice = (struct uart_port_device *)uartport;
    this_driver_data = pci_get_drvdata(uartportdevice->pcidev);
    this_board = this_driver_data->this_board;
    
    DrvDbgPrint(0, "    uart_ops_startup: portid:%u minor:%i \n", uartportdevice->portid, uartportdevice->minor);
    
    // configure uart_port_device
    //uartportdevice->tx_loadsz = ThisModule_uart_config[uartportdevice->uartconfigtype].tx_loadsz;
    uartportdevice->last_ier = 0;
    uartportdevice->last_lcr = 0;
    uartportdevice->last_mcr = 0;
    
    //----
    // hardware reset for uart port (HwdEUART_HardReset)
    //
    {
        /*  reserved, because we dont need this code
        if (this_board->PID == 0xA9610 && uartportdevice->DMAEnable == TRUE)
        {
            // reset DMA and disable DMA
            HwdEUART_HardResetDMA(
                uartportdevice->hHwdEUART
                );
            HwdEUART_InitDMA(
                uartportdevice->hHwdEUART
                );
        }*/
        
        // Clear the FIFO buffers and disable them. (they will be reenabled in set_termios())
        uart_serial_out(uartport, UART_MCR, 0);
        uart_serial_out(uartport, UART_LCR, 0);
        uart_serial_out(uartport, UART_IER, 0);
        
        // reference serial8250_clear_fifos(up);
        uart_serial_out(uartport, UART_FCR, UART_FCR_ENABLE_FIFO);
        uart_serial_out(uartport, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
        uart_serial_out(uartport, UART_FCR, 0);
        
        // clear interrupt.
        uart_serial_in(uartport, UART_LSR);
        uart_serial_in(uartport, UART_RX);
        uart_serial_in(uartport, UART_IIR);
        uart_serial_in(uartport, UART_MSR);
        
        // At this point, there's no way the LSR could still be 0xff;
        // if it is, then bail out, because there's likely no UART here.
        lsr = uart_serial_in(uartport, UART_LSR);
        if (lsr == 0xFF)
        {
            DrvDbgPrint(0, "    %s%i: LSR safety check engaged!\n",
                ThisModule_uart_driver.dev_name,
                uartportdevice->minor
                );
            retval = -ENODEV;
            goto funcexit;
        }
    }
    
    //----
    // link shared_irq_entry to shared_irq_list
    //
    {
        INIT_LIST_HEAD(&uartportdevice->shared_irq_entry);
        
        if (is_real_interrupt(uartport->irq))
        {
            // serial_link_irq_chain()
            this_shared_irq_list = &ThisModule_shared_irq_lists[uartport->irq];
            
            spin_lock_irq(&this_shared_irq_list->lock);
            
            list_add_tail(&uartportdevice->shared_irq_entry, &this_shared_irq_list->head);
            /*{
                if (this_shared_irq_list->head == NULL)
                {
                    this_shared_irq_list->head = &uartportdevice->shared_irq_entry;
                }
                else
                {
                    list_add(&uartportdevice->shared_irq_entry, this_shared_irq_list->head);
                }
            }*/
            
            spin_unlock_irq(&this_shared_irq_list->lock);
            
            DrvDbgPrint(0, "    serial_link_irq_chain minor %i \n", uartportdevice->minor);
        }
    }
    
    //----
    // Now, initialize the UART
    //
    {
        uartportdevice->last_lcr = UART_LCR_WLEN8;
        uart_serial_out(uartport, UART_LCR, uartportdevice->last_lcr);
        
        spin_lock_irqsave(&uartport->lock, flags);
        
        // Most PC uarts need OUT2 raised to enable interrupts.
        if (is_real_interrupt(uartport->irq))
        {
            uartport->mctrl |= TIOCM_OUT2;
        }
        
        //DrvDbgPrint(0, "uart_ops_startup call uart_ops_set_mctrl \n");
        uart_ops_set_mctrl(uartport, uartport->mctrl);
        
        spin_unlock_irqrestore(&uartport->lock, flags);
        
        // Finally, enable interrupts.
        // Note: Modem status interrupts are set via set_termios()
        //
        
        uartportdevice->last_ier = UART_IER_RLSI | UART_IER_RDI;
        uart_serial_out(uartport, UART_IER, uartportdevice->last_ier);
        
        // clear interrupt again.
        uart_serial_in(uartport, UART_LSR);
        uart_serial_in(uartport, UART_RX);
        uart_serial_in(uartport, UART_IIR);
        uart_serial_in(uartport, UART_MSR);
        
        /*  reserved, because we dont need this code
        if (this_board->PID == 0xA9610 && uartportdevice->DMAEnable == TRUE)
        {
            spin_lock_irqsave(&uartport->lock, flags);
            
            //HwdEUART_SetRxSegmentTriggerLevel
            HwdEUART_SetRxSegmentTriggerLevel(
                uartportdevice->hHwdEUART,
                0 //RxSegmentTriggerLevel
                );
            
            //disable ModemStatus Interrupt
            {
                HwcEUARTDMAInterrupt        sHwcEUARTDMAInterrupt;
                
                sHwcEUARTDMAInterrupt.TxSegment = TRUE;
                sHwcEUARTDMAInterrupt.RxSegment = TRUE;
                sHwcEUARTDMAInterrupt.ModemStatus = FALSE;
                sHwcEUARTDMAInterrupt.RxSegmentOverrun = TRUE;
                sHwcEUARTDMAInterrupt.UARTOverrun = TRUE;
                
                HwcEUART_SetDMAInterruptEnable(
                    uartportdevice->hHwdEUART->hHwcEUART,
                    &sHwcEUARTDMAInterrupt
                    );
            }
            
            //HwdEUART_EnableDMA
            HwdEUART_EnableDMA(
                uartportdevice->hHwdEUART
                );
            
            uartportdevice->bops_stop_rx = FALSE;
            uartportdevice->bops_stop_tx = FALSE;
            
            spin_unlock_irqrestore(&uartport->lock, flags);
        }*/
    }
    
    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    //DrvDbgPrint(0, "uart_ops_startup Exit \n");
    
    return retval;
}


void
uart_ops_shutdown(
    struct uart_port *          uartport
    )
{
    struct uart_port_device     *uartportdevice;
    struct pci_driver_data      *this_driver_data;
    struct pci_board            *this_board;
    
    struct shared_irq_list      *this_shared_irq_list;
    
    unsigned long               flags;
                                //irq flags use unsigned long in linux
    
    //DrvDbgPrint(0, "uart_ops_shutdown Entry \n");
    
    //
    // initialize local variables
    //
    
    //
    // check parameters
    //
    
    
    //
    // function body
    //
    
    uartportdevice = (struct uart_port_device *)uartport;
    this_driver_data = pci_get_drvdata(uartportdevice->pcidev);
    this_board = this_driver_data->this_board;
    
    DrvDbgPrint(0, "    uart_ops_shutdown: portid:%u minor:%i \n", uartportdevice->portid, uartportdevice->minor);
    
    //----
    // Disable interrupts from this port
    //
    
    if (this_board->PID == 0xA9610)
    {
        spin_lock_irqsave(&uartport->lock, flags);
        
        // reset DMA and disable DMA
        HwdEUART_HardResetDMA(
            uartportdevice->hHwdEUART
            );
        
        spin_unlock_irqrestore(&uartport->lock, flags);
    }
    
    uartportdevice->last_ier = 0;
    uart_serial_out(uartport, UART_IER, uartportdevice->last_ier);

    spin_lock_irqsave(&uartport->lock, flags);
    
    uartport->mctrl &= ~TIOCM_OUT2;
    
    //DrvDbgPrint(0, "uart_ops_shutdown call uart_ops_set_mctrl \n");
    uart_ops_set_mctrl(uartport, uartport->mctrl);
    
    spin_unlock_irqrestore(&uartport->lock, flags);
    
    //----
    // Disable break condition and FIFOs
    //
    uart_serial_out(uartport, UART_LCR, uart_serial_in(uartport, UART_LCR) & ~UART_LCR_SBC);
    uart_serial_out(uartport, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
    uart_serial_out(uartport, UART_FCR, 0);
    
    //----
    // Read data port to reset things, and then unlink from the IRQ chain.
    //
    
    uart_serial_in(uartport, UART_RX);
    
    //if (!is_real_interrupt(uartport->irq))
      //  del_timer_sync(&up->timer);
    //else
      //  serial_unlink_irq_chain(up);
    
    if (is_real_interrupt(uartport->irq))
    {
        // serial_unlink_irq_chain()
        this_shared_irq_list = &ThisModule_shared_irq_lists[uartport->irq];
        
        spin_lock_irq(&this_shared_irq_list->lock);
        
        list_del(&uartportdevice->shared_irq_entry);
        /*{
            if (!list_empty(ThisModule_shared_irq_lists->head))
            {
                if (ThisModule_shared_irq_lists->head == &uartportdevice->shared_irq_entry)
                {
                    ThisModule_shared_irq_lists->head = ThisModule_shared_irq_lists->head->next;
                }
                list_del(&uartportdevice->shared_irq_entry);
            }
            else
            {
                ThisModule_shared_irq_lists->head = NULL;
            }
        }*/
        
        spin_unlock_irq(&this_shared_irq_list->lock);
    
        DrvDbgPrint(0, "    serial_unlink_irq_chain minor %i \n", uartportdevice->minor);
    }
    
    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    //DrvDbgPrint(0, "uart_ops_shutdown Exit \n");
    
    //return retval;
    return;
}


void
uart_ops_pm(
    struct uart_port *          uartport,
    unsigned int                state,
    unsigned int                oldstate
    )
    // if we supply port power, we can implement power control.
    // after uart_ops_startup, port power set to D0.
    // after uart_ops_shutdown, port power set to D3.
{
    struct uart_port_device     *uartportdevice;
    
    //DrvDbgPrint(0, "uart_ops_pm Entry \n");
    
    //
    // initialize local variables
    //
    
    //
    // check parameters
    //
    
    //
    // function body
    //
    
    uartportdevice = (struct uart_port_device *)uartport;
    
    //DrvDbgPrint(0, "    uart_ops_pm: portid:%u minor:%i \n", uartportdevice->portid, uartportdevice->minor);
    
    //DrvDbgPrint(0, "    uart_ops_pm: state:D%u oldstate:D%u \n", state, oldstate);
    if (state != 0)
    {   
        // prepare port sleep
        // need test!!!
        // keep uart config
        //uart_ops_shutdown(uartport);
    }
    else
    {
        // prepare port wakeup
        // need test!!!
        //uart_ops_startup(uartport);
        // restore uart config
    }
    
    if (uartportdevice->pm)
    {
        // we can store all config setting before sleepping.
        // we can re-store all config setting before re-starting.
        uartportdevice->pm(uartport, state, oldstate);
    }
    
    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    //DrvDbgPrint(0, "uart_ops_pm Exit \n");
    
    //return retval;
    return;
}


void
uart_ops_set_termios(
    struct uart_port *          uartport,
    struct ktermios *           termios,
    struct ktermios *           oldtermios
    )
{
    struct uart_port_device     *uartportdevice;
    struct pci_driver_data      *this_driver_data;
    struct pci_board            *this_board;
    
    unsigned char               lcr;
    
    unsigned int                baud;
    unsigned int                quot;
    
    unsigned long               flags;
                                //irq flags use unsigned long in linux
    
    unsigned int                bShow;
    
    bShow = 0;
    DrvDbgPrintIf(bShow, "uart_ops_set_termios Entry \n");
    
    //
    // initialize local variables
    //
    
    lcr = 0;
    baud = 0;
    quot = 0;
    flags = 0;
    
    //
    // check parameters
    //
    
    //unsigned short c_iflag;		/* input mode flags */
	//unsigned short c_oflag;		/* output mode flags */
	//unsigned short c_cflag;		/* control mode flags */
	//unsigned short c_lflag;		/* local mode flags */
	//unsigned char c_line;		/* line discipline */
    
    //
    // function body
    //
    
    uartportdevice = (struct uart_port_device *)uartport;
    this_driver_data = pci_get_drvdata(uartportdevice->pcidev);
    this_board = this_driver_data->this_board;
    
    DrvDbgPrintIf(bShow, "    uart_ops_set_termios: portid:%u minor:%i \n", uartportdevice->portid, uartportdevice->minor);
    
    switch (termios->c_cflag & CSIZE)
    {
        case CS5:
            lcr = UART_LCR_WLEN5;
            break;
        case CS6:
            lcr = UART_LCR_WLEN6;
            break;
        case CS7:
            lcr = UART_LCR_WLEN7;
            break;
        default: // case CS8:
            lcr = UART_LCR_WLEN8;
            break;
    }
    
    DrvDbgPrintIf(bShow, "    uart_ops_set_termios: word legth = %u \n", lcr + 5);
    
    if (termios->c_cflag & CSTOPB)
    {
        DrvDbgPrintIf(bShow, "    uart_ops_set_termios: stop bit = 2 \n");
        lcr |= UART_LCR_STOP;
    }
    else
    {
        DrvDbgPrintIf(bShow, "    uart_ops_set_termios: stop bit = 1 \n");
    }
    
    if (termios->c_cflag & PARENB)
    {
        DrvDbgPrintIf(bShow, "    uart_ops_set_termios: parity enable \n");
        lcr |= UART_LCR_PARITY;
        
        if (termios->c_cflag & CMSPAR)
        {
            lcr |= UART_LCR_SPAR;
            if (termios->c_cflag & PARODD)
            {
                DrvDbgPrintIf(bShow, "    uart_ops_set_termios: parity mark 0 \n");
            }
            else
            {
                DrvDbgPrintIf(bShow, "    uart_ops_set_termios: parity mark 1 \n");
                lcr |= UART_LCR_EPAR;
            }
        }
        else
        {
            if (termios->c_cflag & PARODD)
            {
                DrvDbgPrintIf(bShow, "    uart_ops_set_termios: parity odd \n");
            }
            else
            {
                DrvDbgPrintIf(bShow, "    uart_ops_set_termios: parity even \n");
                lcr |= UART_LCR_EPAR;
            }
        }
    }
    else
    {
        DrvDbgPrintIf(bShow, "    uart_ops_set_termios: parity disable \n");
    }
    
    
    /*
     * Ask the core to calculate the divisor for us.
     */
    {
        // MaxBaudRate = (SourceClock / ClockDIV).
        baud = uart_get_baud_rate(uartport, termios, oldtermios, 0, uartport->uartclk/uartportdevice->ClockDIV);
        
        //quot = uart_get_divisor(uartport, baud);
        //BRDIV = (SourceClock / ClockDIV) / BaudRate;
        quot = (uartportdevice->ClockRate / uartportdevice->ClockDIV) / baud;
        if (uartportdevice->MinBaudRateDivisor != 0 &&
            quot < uartportdevice->MinBaudRateDivisor)
        {
            quot = uartportdevice->MinBaudRateDivisor;
        }   
    }
    DrvDbgPrintIf(bShow, "    uart_ops_set_termios: baud:%u quot:%u actual baud:%u \n",
        baud, quot, (uartportdevice->ClockRate / uartportdevice->ClockDIV)/quot);
    
    /*
     * Ok, we're now changing the port state.  Do it with
     * interrupts disabled.
     */
    spin_lock_irqsave(&uartport->lock, flags);
    
    if (this_board->PID == 0xA9610 && uartportdevice->DMAEnable == TRUE)
    {
        // reset DMA and disable DMA
        HwdEUART_DisableDMA(uartportdevice->hHwdEUART);
        HwdEUART_HardResetDMA(uartportdevice->hHwdEUART);
        HwdEUART_InitDMA(uartportdevice->hHwdEUART);
    }
    
    
    /*
     * Update the per-port timeout.
     */
    uart_update_timeout(uartport, termios->c_cflag, baud);

    uartport->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
    if (termios->c_iflag & INPCK)
        uartport->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
    if (termios->c_iflag & (BRKINT | PARMRK))
        uartport->read_status_mask |= UART_LSR_BI;
    
    /*
     * Characteres to ignore
     */
    uartport->ignore_status_mask = 0;
    if (termios->c_iflag & IGNPAR)
        uartport->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
    if (termios->c_iflag & IGNBRK) {
        uartport->ignore_status_mask |= UART_LSR_BI;
        /*
         * If we're ignoring parity and break indicators,
         * ignore overruns too (for real raw support).
         */
        if (termios->c_iflag & IGNPAR)
            uartport->ignore_status_mask |= UART_LSR_OE;
    }
    
    /*
     * ignore all characters if CREAD is not set
     */
    if ((termios->c_cflag & CREAD) == 0)
        uartport->ignore_status_mask |= UART_LSR_DR;
    
    /*
     * CTS flow control flag and modem status interrupts
     */
    uartportdevice->last_ier &= ~UART_IER_MSI;
    if (UART_ENABLE_MS(uartport, termios->c_cflag))
        uartportdevice->last_ier |= UART_IER_MSI;

    uart_serial_out(uartport, UART_IER, uartportdevice->last_ier);
    
    uart_serial_out(uartport, UART_LCR, lcr | UART_LCR_DLAB);       // update lcr with enable DLAB
    
    uart_serial_out(uartport, UART_DLL, quot & 0xff);               // LS of divisor
    uart_serial_out(uartport, UART_DLM, quot >> 8);                 // MS of divisor
    
    uart_serial_out(uartport, UART_LCR, lcr);                       // update lcr with disable DLAB
    
    uartportdevice->last_lcr = lcr;                             // Save LCR
    
    uart_serial_out(uartport, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
    uart_serial_out(uartport, UART_FCR, UART_FCR_TRIGGER_14 | UART_FCR_ENABLE_FIFO);    // enable FIFO
    
    if (this_board->PID == 0xA9610 && uartportdevice->DMAEnable == TRUE)
    {
        //HwdEUART_HardResetDMA(uartportdevice->hHwdEUART);
        
        //HwdEUART_InitDMA(uartportdevice->hHwdEUART);
        
        //HwdEUART_SetRxSegmentTriggerLevel
        HwdEUART_SetRxSegmentTriggerLevel(
            uartportdevice->hHwdEUART,
            0 //RxSegmentTriggerLevel
            );
        
        {
            HwcEUARTDMAInterrupt        sHwcEUARTDMAInterrupt;
            
            sHwcEUARTDMAInterrupt.TxSegment = TRUE;
            sHwcEUARTDMAInterrupt.RxSegment = TRUE;
            
            sHwcEUARTDMAInterrupt.RxSegmentOverrun = TRUE;
            sHwcEUARTDMAInterrupt.UARTOverrun = TRUE;
            
            if (UART_ENABLE_MS(uartport, termios->c_cflag))
            {
                //enable ModemStatus Interrupt
                sHwcEUARTDMAInterrupt.ModemStatus = TRUE;
            }
            else
            {
                //disable ModemStatus Interrupt
                sHwcEUARTDMAInterrupt.ModemStatus = FALSE;
            }
            
            HwcEUART_SetDMAInterruptEnable(
                uartportdevice->hHwdEUART->hHwcEUART,
                &sHwcEUARTDMAInterrupt
                );
        }
        
        //HwdEUART_EnableDMAInterrupt(
        //    uartportdevice->hHwdEUART
        //    );
        
        //HwdEUART_EnableDMA
        HwdEUART_EnableDMA(
            uartportdevice->hHwdEUART
            );
        
        uartportdevice->bops_stop_rx = FALSE;
        uartportdevice->bops_stop_tx = FALSE;
    }
    
    spin_unlock_irqrestore(&uartport->lock, flags);
    
    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    DrvDbgPrintIf(bShow, "uart_ops_set_termios Exit \n");
    
    //return retval;
    return;
}


void
uart_ops_set_mctrl(
    struct uart_port *          uartport,
    unsigned int                mctrl
    )
{
    struct uart_port_device     *uartportdevice;
    
    unsigned char               mcr;
    
    //
    // initialize local variables
    //
    
    mcr = 0;
    
    //
    // check parameters
    //
    
    
    //
    // function body
    //
    
    uartportdevice = (struct uart_port_device *)uartport;
    
    //DrvDbgPrint(0, "    uart_ops_set_mctrl: portid:%u minor:%i \n", uartportdevice->portid, uartportdevice->minor);

    if (mctrl & TIOCM_DTR)
        mcr |= UART_MCR_DTR;
        
    if (mctrl & TIOCM_RTS)
        mcr |= UART_MCR_RTS;
    
    if (mctrl & TIOCM_OUT1)
        mcr |= UART_MCR_OUT1;
    
    if (mctrl & TIOCM_OUT2)
        mcr |= UART_MCR_OUT2;
    
    if (mctrl & TIOCM_LOOP)
        mcr |= UART_MCR_LOOP;
    
    uartportdevice->last_mcr = mcr;
    uart_serial_out(uartport, UART_MCR, uartportdevice->last_mcr);
    
    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    //return retval;
    return;
}


unsigned int
uart_ops_get_mctrl(
    struct uart_port *          uartport
    )
    // serial core use spin_lock_irq to protect this function.
    // so we can not use spin_lock_irqsave(&uartport->lock, flags) in this fuction
{
    struct uart_port_device     *uartportdevice;
    
    unsigned int                retval;
    
    unsigned char               msr;
    
    //unsigned long               flags;
                                //irq flags use unsigned long in linux
    
    //
    // initialize local variables
    //
    
    retval = 0;
    
    //
    // check parameters
    //
    
    
    //
    // function body
    //
    
    uartportdevice = (struct uart_port_device *)uartport;
    
    //DrvDbgPrint(0, "    uart_ops_get_mctrl: portid:%u minor:%i \n", uartportdevice->portid, uartportdevice->minor);
    
    msr = check_modem_status(uartport);
    
    //msr = uart_serial_in(uartport, UART_MSR);

    retval = 0;
    
    if (msr & UART_MSR_CTS)
        retval |= TIOCM_CTS;
    
    if (msr & UART_MSR_DSR)
        retval |= TIOCM_DSR;
    
    if (msr & UART_MSR_RI)
        retval |= TIOCM_RNG;
                
    if (msr & UART_MSR_DCD)
        retval |= TIOCM_CAR;
    
    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    return retval;
    //return;
}


unsigned int
uart_ops_tx_empty(
    struct uart_port *          uartport
    )
{
    struct uart_port_device     *uartportdevice;
    struct pci_driver_data      *this_driver_data;
    struct pci_board            *this_board;
    
    unsigned int                retval;
    
    unsigned long               flags;
                                //irq flags use unsigned long in linux
    
    //
    // initialize local variables
    //
    
    retval = 0;
    
    //
    // check parameters
    //
    
    
    //
    // function body
    //
    
    uartportdevice = (struct uart_port_device *)uartport;
    this_driver_data = pci_get_drvdata(uartportdevice->pcidev);
    this_board = this_driver_data->this_board;
    
    //DrvDbgPrint(0, "    uart_ops_tx_empty: portid:%u minor:%i \n", uartportdevice->portid, uartportdevice->minor);
    
    spin_lock_irqsave(&uartport->lock, flags);
    
    if (this_board->PID == 0xA9610 && uartportdevice->DMAEnable == TRUE)
    {
        //if (TxSegment0Ready == 0 && TxSegment1Ready == 0)
        ULONG                       bAllEmpty;
        HwdEUART_DMAAllEmpty(
            uartportdevice->hHwdEUART,
            &bAllEmpty
            );
        
        if (bAllEmpty == TRUE)
        {
            retval = TIOCSER_TEMT;
        }
        else
        {
            retval = 0;
        }
    }
    else
    {
        retval = uart_serial_in(uartport, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
    }
    
    spin_unlock_irqrestore(&uartport->lock, flags);

    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    return retval;
    //return;
}


void
uart_ops_start_tx(
    struct uart_port *          uartport
    )
    // serial core use spin_lock_irq to protect this function.
    // so we can not use spin_lock_irqsave(&uartport->lock, flags) in this fuction
{
    struct uart_port_device     *uartportdevice;
    struct pci_driver_data      *this_driver_data;
    struct pci_board            *this_board;
    
    
    //
    // initialize local variables
    //
    
    //
    // check parameters
    //
    
    //
    // function body
    //
    
    uartportdevice = (struct uart_port_device *)uartport;
    this_driver_data = pci_get_drvdata(uartportdevice->pcidev);
    this_board = this_driver_data->this_board;
    
    //DrvDbgPrint(0, "    uart_ops_start_tx: portid:%u minor:%i \n", uartportdevice->portid, uartportdevice->minor);
    
    if (this_board->PID == 0xA9610 && uartportdevice->DMAEnable == TRUE)
    {
        ULONG                       bTxEmpty;
        
        uartportdevice->last_ier |= UART_IER_THRI;
        uartportdevice->bops_stop_tx = FALSE;
        
        HwdEUART_DMATxEmpty(
            uartportdevice->hHwdEUART,
            &bTxEmpty
            );
        if (bTxEmpty == TRUE)
        {
            transmit_chars_DMA(uartport);
        }
        else
        {
            //DrvDbgPrint(0, "    uart_ops_start_tx: bTxEmpty \n");
            //HwdEUART_DMAShowAll(uartportdevice->hHwdEUART);
        }
    }
    else
    {
        if (!(uartportdevice->last_ier & UART_IER_THRI))
        {
            uartportdevice->last_ier |= UART_IER_THRI;
            uart_serial_out(uartport, UART_IER, uartportdevice->last_ier);
        }
    }

    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    //return retval;
    return;
}


void
uart_ops_stop_tx(
    struct uart_port *          uartport
    )
    // serial core use spin_lock_irq to protect this function.
    // so we can not use spin_lock_irqsave(&uartport->lock, flags) in this fuction
{
    struct uart_port_device     *uartportdevice;
    struct pci_driver_data      *this_driver_data;
    struct pci_board            *this_board;
    
    //
    // initialize local variables
    //
    
    //
    // check parameters
    //
    
    
    //
    // function body
    //
    
    uartportdevice = (struct uart_port_device *)uartport;
    this_driver_data = pci_get_drvdata(uartportdevice->pcidev);
    this_board = this_driver_data->this_board;
    
    //DrvDbgPrint(0, "    uart_ops_stop_tx: portid:%u minor:%i \n", uartportdevice->portid, uartportdevice->minor);
    
    if (this_board->PID == 0xA9610 && uartportdevice->DMAEnable == TRUE)
    {
        uartportdevice->last_ier &= ~UART_IER_THRI;
        uartportdevice->bops_stop_tx = TRUE;
    }
    else
    {
        if (uartportdevice->last_ier & UART_IER_THRI)
        {
            uartportdevice->last_ier &= ~UART_IER_THRI;
            uart_serial_out(uartport, UART_IER, uartportdevice->last_ier);
        }
    }
    
    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    //return retval;
    return;
}


void
uart_ops_stop_rx(
    struct uart_port *          uartport
    )
    // disable LineStatus Interrupt
    // serial core use this function on uart_close() and uart_suspend()
    // serial core use spin_lock_irq to protect this function.
    // so we can not use spin_lock_irqsave(&uartport->lock, flags) in this fuction
{
    struct uart_port_device     *uartportdevice;
    struct pci_driver_data      *this_driver_data;
    struct pci_board            *this_board;
    
    //
    // initialize local variables
    //
    
    //
    // check parameters
    //
    
    
    //
    // function body
    //
    
    uartportdevice = (struct uart_port_device *)uartport;
    this_driver_data = pci_get_drvdata(uartportdevice->pcidev);
    this_board = this_driver_data->this_board;
    
    //DrvDbgPrint(0, "    uart_ops_stop_rx: portid:%u minor:%i \n", uartportdevice->portid, uartportdevice->minor);
    
    uartport->read_status_mask &= ~UART_LSR_DR;
    
    if (this_board->PID == 0xA9610 && uartportdevice->DMAEnable == TRUE)
    {
        uartportdevice->last_ier &= ~UART_IER_RLSI;
        uartportdevice->bops_stop_rx = TRUE;
    }
    else
    {
        uartportdevice->last_ier &= ~UART_IER_RLSI;
        uart_serial_out(uartport, UART_IER, uartportdevice->last_ier);
    }
    
    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    //return retval;
    return;
}


void
uart_ops_enable_ms(
    struct uart_port *          uartport
    )
    // enable ModemStatus Interrupt
    // serial core use this function to enable ModemStatus Interrupt.
{
    struct uart_port_device     *uartportdevice;
    struct pci_driver_data      *this_driver_data;
    struct pci_board            *this_board;
    
    //
    // initialize local variables
    //
    
    //
    // check parameters
    //
    
    
    //
    // function body
    //
    
    uartportdevice = (struct uart_port_device *)uartport;
    this_driver_data = pci_get_drvdata(uartportdevice->pcidev);
    this_board = this_driver_data->this_board;
    
    //DrvDbgPrint(0, "    uart_ops_enable_ms: portid:%u minor:%i \n", uartportdevice->portid, uartportdevice->minor);
    
    if (this_board->PID == 0xA9610 && uartportdevice->DMAEnable == TRUE)
    {
        uartportdevice->last_ier |= UART_IER_MSI;
        {
            HwcEUARTDMAInterrupt        sHwcEUARTDMAInterrupt;
            
            
            sHwcEUARTDMAInterrupt.TxSegment = TRUE;
            sHwcEUARTDMAInterrupt.RxSegment = TRUE;
            sHwcEUARTDMAInterrupt.RxSegmentOverrun = TRUE;
            sHwcEUARTDMAInterrupt.UARTOverrun = TRUE;
            
            //enable ModemStatus Interrupt
            sHwcEUARTDMAInterrupt.ModemStatus = TRUE;
            
            HwcEUART_SetDMAInterruptEnable(
                uartportdevice->hHwdEUART->hHwcEUART,
                &sHwcEUARTDMAInterrupt
                );
        }
        
    }
    else
    {
        uartportdevice->last_ier |= UART_IER_MSI;
        uart_serial_out(uartport, UART_IER, uartportdevice->last_ier);
    }
    
    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    //return retval;
    return;
}


void
uart_ops_break_ctl(
    struct uart_port *          uartport,
    int                         break_state
    )
{
    struct uart_port_device     *uartportdevice;
    
    unsigned long               flags;
                                //irq flags use unsigned long in linux
    
    //
    // initialize local variables
    //
    
    //
    // check parameters
    //
    
    
    //
    // function body
    //
    
    uartportdevice = (struct uart_port_device *)uartport;
    
    //DrvDbgPrint(0, "    uart_ops_break_ctl: portid:%u minor:%i \n", uartportdevice->portid, uartportdevice->minor);
    
    //DrvDbgPrint(0, "    uart_ops_break_ctl: break_state:%i \n", break_state);
    spin_lock_irqsave(&uartport->lock, flags);
    if (break_state == -1)
        uartportdevice->last_lcr |= UART_LCR_SBC;
    else
        uartportdevice->last_lcr &= ~UART_LCR_SBC;
    uart_serial_out(uartport, UART_LCR, uartportdevice->last_lcr);
    spin_unlock_irqrestore(&uartport->lock, flags);
    
    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    //return retval;
    return;
}



int
uart_ops_ioctl(
    struct uart_port *          uartport,
    unsigned int                cmd,
    unsigned long               arg
    )
{
    struct uart_port_device     *uartportdevice;
    struct pci_driver_data      *this_driver_data;
    struct pci_board            *this_board;
    
    unsigned int                retval;
    
    unsigned int                BusNumber;
    unsigned int                DeviceNumber;
    unsigned int                FunctionNumber;
    
    unsigned int                Port422485;
    
    struct ioctl_param_PortInfo param_PortInfo;
    struct ioctl_param_ConfigA9610  param_ConfigA9610;
    
    unsigned int                ClockRate;
    unsigned int                ClockDIV;
    unsigned int                MinBaudRateDivisor;
    
    //struct ioctl_rw_arg         ioctlrwarg;
    
    //
    // initialize local variables
    //
    
    retval = 0;
    
    //
    // check parameters
    //
    
    //
    // function body
    //
    
    uartportdevice = (struct uart_port_device *)uartport;
    this_driver_data = pci_get_drvdata(uartportdevice->pcidev);
    this_board = this_driver_data->this_board;
    
    // get pci bus information.
    BusNumber = uartportdevice->pcidev->bus->number;
    DeviceNumber = PCI_SLOT(uartportdevice->pcidev->devfn);
    FunctionNumber = PCI_FUNC(uartportdevice->pcidev->devfn);
    
    //DrvDbgPrint(0, "    uart_ops_ioctl: portid:%u minor:%i \n", uartportdevice->portid, uartportdevice->minor);
    
    switch (cmd)
    {
        case IOCTL_PortInfo:
        {
            if (copy_from_user(&param_PortInfo, (void *)arg, sizeof(struct ioctl_param_PortInfo)))
            {
                retval = -EFAULT;
            }
            else
            {
                param_PortInfo.BusNumber = BusNumber;
                param_PortInfo.DeviceNumber = DeviceNumber;
                param_PortInfo.FunctionNumber = FunctionNumber;
                param_PortInfo.PortType = uartportdevice->uartconfigtype;
                param_PortInfo.PortID = uartportdevice->portid;
                param_PortInfo.ClockRate = uartportdevice->ClockRate;
                param_PortInfo.ClockDIV = uartportdevice->ClockDIV;
                param_PortInfo.DMAEnable = uartportdevice->DMAEnable;
                if (copy_to_user((void *)arg, &param_PortInfo, sizeof(struct ioctl_param_PortInfo)))
                {
                    retval = -EFAULT;
                }
            }
        }
        break;
        
        case IOCTL_Goto422Mode:
        {
            if (this_board->vendor == 0x1bbc && this_board->uartconfigtype == EUART_422485_V1)
            {
                Port422485 = 0;
                ReadPCIConfiguration(
                    BusNumber,
                    DeviceNumber,
                    FunctionNumber,
                    0x42, // PCI offset 0x42 is 422mode register.
                    1,
                    &Port422485
                    );
            
                Port422485 |= (0x01 << uartportdevice->portid);
                
                WritePCIConfiguration(
                    BusNumber,
                    DeviceNumber,
                    FunctionNumber,
                    0x42, // PCI offset 0x42 is 422mode register.
                    1,
                    &Port422485
                    );
            
                DrvDbgPrint(0, "uart_ops_ioctl: portid:%u minor:%i set to 422mode \n", uartportdevice->portid, uartportdevice->minor);
            }
            retval = -ENOIOCTLCMD;
        }
        break;
        
        case IOCTL_Goto485Mode:
        {
            if (this_board->vendor == 0x1bbc && this_board->uartconfigtype == EUART_422485_V1)
            {
                Port422485 = 0;
                ReadPCIConfiguration(
                    BusNumber,
                    DeviceNumber,
                    FunctionNumber,
                    0x42, // PCI offset 0x42 is 422mode register.
                    1,
                    &Port422485
                    );
            
                Port422485 &= ~(0x01 << uartportdevice->portid);
                
                WritePCIConfiguration(
                    BusNumber,
                    DeviceNumber,
                    FunctionNumber,
                    0x42, // PCI offset 0x42 is 422mode register.
                    1,
                    &Port422485
                    );
            
                DrvDbgPrint(0, "uart_ops_ioctl: portid:%u minor:%i set to 485mode \n", uartportdevice->portid, uartportdevice->minor);
            }
            
            retval = -ENOIOCTLCMD;
        }
        break;
        
        case IOCTL_ConfigA9610:
        {
            if (this_board->PID == 0xA9610)
            {
                if (copy_from_user(&param_ConfigA9610, (void *)arg, sizeof(struct ioctl_param_ConfigA9610)))
                {
                    retval = -EFAULT;
                }
                else
                {
                    switch (param_ConfigA9610.UARTSpeedMode)
                    {
                        case 1:
                        {
                            ClockRate = 24000000;
                            ClockDIV = 16;
                            MinBaudRateDivisor = 1;
                        }
                        break;
                        
                        case 4:
                        {
                            ClockRate = 1846153;
                            ClockDIV = 8;
                            MinBaudRateDivisor = 1;
                        }
                        break;
                        
                        case 5:
                        {
                            ClockRate = 24000000;
                            ClockDIV = 13;
                            MinBaudRateDivisor = 1;
                        }
                        break;
                        
                        case 6:
                        {
                            ClockRate = 50000000;
                            ClockDIV = 1;
                            MinBaudRateDivisor = 5;
                        }
                        break;
                        
                        default:
                        {
                            ClockRate = 1846153;
                            ClockDIV = 16;
                            MinBaudRateDivisor = 1;
                        }
                        break;
                    }
                    
                    uartportdevice->ClockRate = ClockRate;
                    uartportdevice->ClockDIV = ClockDIV;
                    uartportdevice->MinBaudRateDivisor = MinBaudRateDivisor;
                    uartportdevice->DMAEnable = param_ConfigA9610.DMAEnable;
                    
                    PCIConfigure_A9610(
                        BusNumber,
                        DeviceNumber,
                        FunctionNumber,
                        uartportdevice->portid,
                        param_ConfigA9610.UARTSpeedMode
                        );
                    
                    uart_ops_config_port(uartport, 0);
                }
            }
            else
            {
                retval = -ENOIOCTLCMD;
            }
            
        }
        break;
        
        
        
        /*case EXAR_READ_REG:
        {
            if (copy_from_user(&ioctlrwarg, (void *)arg, sizeof(ioctlrwarg)))
                return -EFAULT;
            ioctlrwarg.regvalue = serial_inp(up, ioctlrwarg.reg);
            if (copy_to_user((void *)arg, &ioctlrwarg, sizeof(ioctlrwarg)))
                return -EFAULT;
            ret = 0;
        }
        break;
        
        case EXAR_WRITE_REG:
        {
            if (copy_from_user(&ioctlrwarg, (void *)arg, sizeof(ioctlrwarg)))
                return -EFAULT;
            serial_outp(up, ioctlrwarg.reg, ioctlrwarg.regvalue);
            ret = 0;
        }
        break;*/
        
        default:
        {
            //DrvDbgPrint(0, "uart_ops_ioctl: unknown ioctl command 0x%x \n", cmd);
            retval = -ENOIOCTLCMD;
        }
        break;
    }
    
    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    return retval;
    //return;
}


VOID
PCIConfigure_A9610(
    IN      ULONG               BusNumber,
    IN      ULONG               DeviceNumber,
    IN      ULONG               FunctionNumber,
    IN      ULONG               PortID,
    IN      ULONG               UARTSpeedMode
    )
{
    //NTSTATUS                    ntstatus;
    ULONG                       result;
    
    //ULONG                       PortID;
    ULONG                       PortConfigure;
    
    //
    // initialize local variables
    //
    
    //ntstatus = STATUS_SUCCESS;
    
    //
    // check parameters
    //
    
    //
    // function body
    //
    
    //PortID = pFdoDeviceExtension->PortID;

    // see A9610 spec.
    
    ReadPCIConfiguration(
        BusNumber,
        DeviceNumber,
        FunctionNumber,
        0x60 + PortID,
        1,
        &PortConfigure
        );
    
    PortConfigure &= 0xF8;
    
    switch (UARTSpeedMode)
    {
        case 1:
        {
            PortConfigure |= 0x01;
        }
        break;
        
        case 4:
        {
            PortConfigure |= 0x04;
        }
        break;
        
        case 5:
        {
            PortConfigure |= 0x05;
        }
        break;
        
        case 6:
        {
            PortConfigure |= 0x06;
        }
        break;
        
        default:
        {
            //PortConfigure &= 0xF8;
        }
        break;
    }
    
    WritePCIConfiguration(
        BusNumber,
        DeviceNumber,
        FunctionNumber,
        0x60 + PortID,
        1,
        &PortConfigure
        );
    

    goto funcexit;
funcexit:

    //
    // free local buffers
    //
    
    //
    // unsuccessful handle
    //
    
    ;
    //return ntstatus;
}