/*
* Zhiyuan PCI/PCIE Device
* Copyright (C) Guangzhou ZHIYUAN Electronics Co., Ltd. All rights reserved.
*/
#include "common.h"
#include "pcidev_manage.h"
#include "pcie_cdma_cfg.h"
#include <linux/delay.h>

unsigned dbg = 0;
module_param(dbg, uint, 0644);

// const int trigger_offset[2] = {0x04, 0x10};
// #define INT_TRIGGER(pdev,state) REG_W(pdev->bars[1].base,trigger_offset[pdev->devIndex],state)
#define INT_STATE(pdev)         (REG_R(pdev->bars[1].base,0x0C)&0xFF)
#define INT_CLEAR(pdev,state)   REG_W(pdev->bars[1].base,0x08,state)   

/************************************************************/
/* type define */


/************************************************************/
/* global var */
static const struct pci_device_id zpcidev_id_table[] = {
    {0x1feb, 0x73e0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
    {0}
};
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION(ZPCIDEV_MODULE_INFO);
MODULE_DEVICE_TABLE(pci, zpcidev_id_table);

enum { PCIE_ETHERCAT = 0, ZPCIDEV_HW_TYPES};
int zpcidev_ports[] = {1};

static DEFINE_MUTEX(gDriverLock);
static DriverGlobalRes gDriverRes;

static int (*zpcidev_cdma_run_if[])(zpcidev_device *d, const PCIe_CDMA_MSG *cdmaMsg) = {
    _zpcidev_cdma_pc_read_from_device,
    _zpcidev_cdma_pc_write_to_device,
    _zpcidev_cdma_device_copy_to_device,
};

static const struct file_operations zpcidev_manage_fops = {
    .open = zpcidev_manage_f_open,
    .release = zpcidev_manage_f_close,
    .unlocked_ioctl = zpcidev_manage_f_ioctl
};

// device_info_t device_info;

/************************************************************/
#define ZPCIDEV_MUTEX_INIT(m)    init_waitqueue_head(&(m)->wait)
#define ZPCIDEV_MUTEX_LOCK(m)    wait_event((m)->wait, !atomic_cmpxchg(&(m)->locked, 0, 1))
#define ZPCIDEV_MUTEX_UNLOCK(m)  atomic_xchg(&(m)->locked, 0),wake_up(&(m)->wait)


__u32 zpcidev_get_board_cnt(void){
    __u32 boardCnt;
    mutex_lock(&gDriverLock);
    boardCnt = gDriverRes.deviceNum;
    mutex_unlock(&gDriverLock);
    return boardCnt;
}

__s32 zpcidev_get_driver_version_msg(void* outputBuff, __u32 buffSize){
    __s32 ret = 0;
    __u32 copySize = 0 ;
    memset(outputBuff, 0, buffSize);

    mutex_lock(&gDriverLock);
    copySize = strlen(gDriverRes.caDriverVersion);
    if (copySize > buffSize){
        ret = -1;
        copySize = buffSize - 1;
    }
    memcpy(outputBuff, gDriverRes.caDriverVersion, copySize);
    mutex_unlock(&gDriverLock);
    return ret;
}

static __inline void _zpcidev_print_bar_msg(__u32 i, size_t start, size_t end, size_t size, void __iomem* base){
    _MSG_("bar%d phyAddr: [0x%lX ~ 0x%lX], len=0x%lX(%lu)",
        i, start, end, size, size);
}

static void zpcidev_map_bars(zpcidev_device *d)
{
    int i;
    for (i = 0; i < ZPCIDEV_MAX_BARS; i++) {
        if (!pci_resource_len(d->pcidev, i)) continue;
        d->bars[i].start = pci_resource_start(d->pcidev, i);
        d->bars[i].end = pci_resource_end(d->pcidev, i);
        d->bars[i].size = d->bars[i].end + 1 - d->bars[i].start;
        d->bars[i].base = pci_iomap(d->pcidev, i, d->bars[i].size);
        _zpcidev_print_bar_msg(i, d->bars[i].start, d->bars[i].end, d->bars[i].size, d->bars[i].base);
    }
}
static void zpcidev_unmap_bars(zpcidev_device *d)
{
    int i;
    for (i = 0; i < ZPCIDEV_MAX_BARS; i++) {
        if (!d->bars[i].base) continue;
        // _zpcidev_print_bar_msg(i, d->bars[i].start, d->bars[i].end, d->bars[i].size, d->bars[i].base);
        pci_iounmap(d->pcidev, d->bars[i].base);
        d->bars[i].base = NULL;
    }
}

/**
 * @brief check device type. Suitable for matching multiple devices with one driver
 */
static int zpcidev_hw_detect(struct pci_dev *dev)
{
    int ret, i;
    u16 vendor, device;
    ret = pci_read_config_word(dev, PCI_VENDOR_ID, &vendor);
    if (ret) {
        _ERR_("pci_read_config_word(PCI_VENDOR_ID) failed");
        return ret;
    }
    ret = pci_read_config_word(dev, PCI_DEVICE_ID, &device);
    if (ret) {
        _ERR_("pci_read_config_word(PCI_DEVICE_ID) failed");
        return ret;
    }
    for (i = 0; i < ZPCIDEV_HW_TYPES; i++) {
        if (vendor == zpcidev_id_table[i].vendor && device == zpcidev_id_table[i].device)
            return i;
    }
    _ERR_("unknown device: vendor=0x%04x, device=0x%04x", vendor, device);
    return -1;
}

/** 
 * @brief print flag
 */
static void zpcidev_hw_init(zpcidev_device *d, int ctx)
{
    _MSG_("*** deviceType=%d, ctx=%d", d->deviceType, ctx);
}

static int zpcidev_f_open(struct inode *inode, struct file *file)
{
    zpcidev_device *d = file_to_dev(file);
    _DBG_("+++ deviceType=%d", d->deviceType);
    _DBG_("---");
    return 0;
}

static int zpcidev_f_close(struct inode *inode, struct file *file)
{
    zpcidev_device *pciDev = file_to_dev(file);
    _DBG_("+++ deviceType=%d", pciDev->deviceType);
    
    _DBG_("---");
    return 0;
}

static long zpcidev_cdma_copy(struct file *file, unsigned long arg){
    int ret = 0;
    PCIe_CDMA_MSG cdmaMsg;
    zpcidev_device *d = file_to_dev(file);
    _DBG_("+++ %s +++", d->miscDevName);
    do{
        ret = copy_from_user(&cdmaMsg, (void*)arg, sizeof(PCIe_CDMA_MSG));
        if (ret) break;
        if (cdmaMsg.copyMode < CDMA_COPY_MODE_SIZE && cdmaMsg.dataSize <= d->dmaHandle.dma_size){
            ret = zpcidev_cdma_wait_over(d, cdmaMsg.timeout_ms);
            if (ret) break;
            ret = zpcidev_cdma_run_if[cdmaMsg.copyMode](d, &cdmaMsg);
        }else{
            ret = -1;
        }
    }while (0);
    _DBG_("---");
    return ret;
}

static long zpcidev_cdma_info(struct file *file, unsigned long arg){
    int ret = 0;
    PCIe_CMDA_INFO localData;
    zpcidev_device *pciDev = file_to_dev(file);
    _DBG_("+++ %s +++", pciDev->miscDevName);
    localData.dmaSize = pciDev->dmaHandle.dma_size;
    localData.dmaVa = (uintptr_t)pciDev->dmaHandle.dma_va;
    localData.dmaPa = pciDev->dmaHandle.dma_pa;
    do{
        ret = copy_to_user((void*)arg, &localData, sizeof(PCIe_CMDA_INFO));
    }while (0);

    _DBG_("---");
    return ret;
}

// static long __test_if(void* args){
//     __u32 index = 0;
//     __testMsg testMsg;
//     long ret = 0;
//     ret = copy_from_user(&testMsg, args, sizeof(__testMsg));
//     if (ret) {
//         _ERR_("copy_from_user failed(%d).", ret);
//         return ret;
//     }

//     phys_addr_t phys_addr = testMsg.phyAddr;  // 32位物理地址
//     size_t size = testMsg.length;  // 映射的内存大小
//     printk("ioremap phy:0x%08lX size:0x%08lX", phys_addr, size);
//     void *virt_addr = ioremap(phys_addr, size);
//     if (!virt_addr) {
//         // 映射失败处理
//         printk("[%s:%d]:__ioremap failed.", __FUNCTION__, __LINE__);
//         return -ENOMEM;
//     }

//     for (; index < 32; index++){
//         printk("0x%08X: 0x%02X", index, *((__u8*)virt_addr + index));
//     }

//     return 0;
//     // 将virt_addr作为DMA操作的地址
// }

static long _zpcidev_f_ioctl_waitInt(zpcidev_device *d, unsigned long _arg){
    void *arg = (void*)_arg;
    wait_sem_t wait_sem;
    do{
        long ret = copy_from_user(&wait_sem, arg, sizeof(wait_sem));
        if (ret) {
            _DBG_("IOCTL_WAIT_INT:copy_from_user(opt) failed");
            ret = -1;
            break;
        }
        if (wait_sem.interrupt < 8){
            wait_sem.result = 0;
            _DBG_("IOCTL_WAIT_INT:sem(%d:%lld).", wait_sem.interrupt, wait_sem.timeout);
            if ((wait_sem.result = down_timeout(&d->semHr[wait_sem.interrupt].sem, msecs_to_jiffies(wait_sem.timeout))) < 0){
                wait_sem.result = -ETIMEDOUT;
            }
            else {
                while(down_trylock(&d->semHr[wait_sem.interrupt].sem) == 0);
            }
            _DBG_("IOCTL_WAIT_INT:sem over.result:%d", wait_sem.result);
            ret = copy_to_user(arg, &wait_sem, sizeof(wait_sem));
        }else{
            _DBG_("IOCTL_WAIT_INT:interrupt over range(0~7),cur value:%u", wait_sem.interrupt);
        }
    }while (0);
    return 0;
}

/*
static long zpcidev_f_ioctl_rw(const zpcidev_device *d, unsigned int cmd, unsigned long _arg){
    long ret = 0;
    do{
        void *arg = (void*)_arg;
        zpcidev_reg_rw rw;
        ret = copy_from_user(&rw, arg, sizeof(rw));
        if (ret) {
            _DBG_("IOCTL_REG_W/IOCTL_REG_R:copy_from_user(opt) failed");
            ret = -1;
            break;
        }
        ZPCIDEV_MUTEX_LOCK(&d->lock);
        {
            u32 *reg = (u32*)(d->bars[0].base + rw.addr);
            u32 *buf = (u32*)rw.buff;
            u32 cnt = rw.size >> 2;
            u32 tmp[64];
            u32 n, i;
            bool write = cmd == IOCTL_REG_W;
            for (n = 0; cnt; cnt -= n, buf += n) {
                n = min(cnt, 64u);
                if (write) {
                    // copy from user
                    ret = copy_from_user(tmp, buf, n*sizeof(u32));
                    if (ret) {
                        _DBG_("copy_from_user(dat) failed");
                        ret = -EFAULT;
                        break;
                    }
                    // write to hw
                    if (rw.fifo) {
                        for (i = 0; i < n; i++) {
                            writel(tmp[i], reg);
                        }
                    } else {
                        for (i = 0; i < n; i++, reg++) {
                            writel(tmp[i], reg);
                        }
                    }
                } else {
                    // read from hw
                    if (rw.fifo) {
                        for (i = 0; i < n; i++) {
                            tmp[i] = readl(reg);
                        }
                    } else {
                        for (i = 0; i < n; i++, reg++) {
                            tmp[i] = readl(reg);
                        }
                    }
                    // copy to user
                    ret = copy_to_user(buf, tmp, n*sizeof(u32));
                    if (ret) {
                        _DBG_("copy_to_user(dat) failed");
                        ret = -EFAULT;
                        break;
                    }
                }
            }
        }
        ZPCIDEV_MUTEX_UNLOCK(&d->lock);
    }while (0);
    return ret;
}
*/

static long zpcidev_f_ioctl_get_bar_msg(const zpcidev_device *d, unsigned long _arg){
    long ret = 0;
    do{
        void *arg = (void*)_arg;
        PCIE_BAR_MSG bar_msg;
        int i;

        for (i = 0; i < DEV_BAR_NUM; i++) {
            if (!d->bars[i].base) continue;
            bar_msg.barPtr[i] = (size_t)d->bars[i].start;
            bar_msg.barSize[i] = d->bars[i].size;
            _DBG_("bar%d: 0x%08x %08x, len=0x%x(%u)",
                i, (unsigned)(bar_msg.barPtr[i] >> 32), (unsigned)bar_msg.barPtr[i],
                (unsigned)bar_msg.barSize[i], (unsigned)bar_msg.barSize[i]);
        }

        ret = copy_to_user(arg, &bar_msg, sizeof(PCIE_BAR_MSG));
        if(ret) {
            _DBG_("copy_to_user(dat) failed");
            ret = -EFAULT;
            break;
        }
    }while (0);
    return ret;
}

static long zpcidev_f_ioctl(struct file *file, unsigned int cmd, unsigned long _arg)
{
    zpcidev_device *pciDev = file_to_dev(file);
    int ret = -1;
    //ktime_t ktime;
    _DBG_("+++ %s +++", pciDev->miscDevName);
    switch (cmd) {
    case IOCTL_CDMA_COPY:{
        ret = zpcidev_cdma_copy(file, _arg);
        break;
    }
    case IOCTL_WAIT_INT:{
        ret = _zpcidev_f_ioctl_waitInt(pciDev, _arg);
        break;
    }
    case IOCTL_CDMA_INFO:{
        ret = zpcidev_cdma_info(file, _arg);
        break;
    }
    // case IOCTL_REG_W:
    // case IOCTL_REG_R:{
    //     ret = zpcidev_f_ioctl_rw(d, cmd, _arg);
    //     break;
    // }
    case IOCTL_GET_BAR_MSG: {
        ret = zpcidev_f_ioctl_get_bar_msg(pciDev, _arg);
        break;
    }
    case IOCTL_CTRL_0:{
        // ret = __test_if(_arg);
        _WRN_("mutex lock");
        mutex_lock(&pciDev->dmaHandle.dma_mtx);
        msleep(10000);
        _WRN_("mutex unlock");
        mutex_unlock(&pciDev->dmaHandle.dma_mtx);
        ret = 0;
        break;
    }
    // case IOCTL_CTRL_1:{
    //     ret = zpcidev_cdma_stop(file);
    //     break;
    // }
    
    default:
        break;
    }
    _DBG_("--- %s:ret=%d ---", pciDev->miscDevName, ret);
    return ret;
}

static int zpcidev_f_mmap(struct file *filp, struct vm_area_struct *vma){
    unsigned long pfn = 0;
    unsigned long size = 0;
    int ret;
    zpcidev_device *d;
    d = file_to_dev(filp);
    if (vma->vm_pgoff == 0){
        pfn = ((d->bars[0].start) >> PAGE_SHIFT);
    }else if (vma->vm_pgoff == 1){
        pfn = ((d->bars[1].start) >> PAGE_SHIFT);
    }else if (vma->vm_pgoff == 2){
        pfn = ((d->bars[2].start) >> PAGE_SHIFT);
    }else{
        _DBG_("--- bar[%lu] err---", vma->vm_pgoff);
        return -1;
    }
    size = vma->vm_end - vma->vm_start;
    ret = remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot);
    return ret;
}

static const struct file_operations zpcidev_fops = {
    .open = zpcidev_f_open,
    .release = zpcidev_f_close,
    .llseek = no_llseek,
    .unlocked_ioctl = zpcidev_f_ioctl,
    .mmap = zpcidev_f_mmap
};

irqreturn_t zpcidev_isr(int irq, zpcidev_device *d)
{
    irqreturn_t ret = IRQ_NONE;
    __u32 state, i, temp;
    _DBG_("%s interrupt.", d->miscDevName);
    spin_lock(&d->InterruptMsg.locked);

    temp = state = INT_STATE(d);
    if (state){
        for (i = 0; i < SEM_SIZE && temp; i++){
            if (temp & 1){
                up(&d->semHr[i].sem);
            }
            temp >>= 1;
        }
        INT_CLEAR(d, state);
        ret = IRQ_HANDLED;
    }else{
        _DBG_("other isr trigger.");
    }
    spin_unlock(&d->InterruptMsg.locked);

    return ret;
}

static void zpcidev_miscdev_deregister(zpcidev_device *d)
{
    _DBG_("+++");
    if (d->miscdev_rdy)
        misc_deregister(&d->miscDev);
    d->miscdev_rdy = false;
    _DBG_("---");
}

int zpcidev_manage_miscdev_deregister(zpcidev_device *pcidev)
{
    gDriverRes.deviceNum--;
    _DBG_("+++ device num: %d", gDriverRes.deviceNum);
    pcidev->boardNode->next->prev = pcidev->boardNode->prev;
    pcidev->boardNode->prev->next = pcidev->boardNode->next;
    kfree(pcidev->boardNode);
    if (!gDriverRes.deviceNum){
        misc_deregister(&gDriverRes.manageDev.dev);
    }
    _DBG_("---");
    return 0;
}

static void zpcidev_remove(struct pci_dev *dev)
{
    zpcidev_device *pcidev = pci_get_drvdata(dev);
    _MSG_("+++ remove device node: /dev/%s +++", pcidev->miscDevName);
    pci_set_drvdata(dev, NULL);
    mutex_lock(&gDriverLock);
    if (pcidev) {
        _zpcidev_cdma_release(pcidev);
        if (pcidev->irq != -1) free_irq(pcidev->irq, pcidev);
        zpcidev_miscdev_deregister(pcidev);
        zpcidev_unmap_bars(pcidev);
        _DBG_("kfree(device)");
        kfree(pcidev);
    }
    zpcidev_manage_miscdev_deregister(pcidev);
    
    _DBG_("pci_release_regions");
    pci_release_regions(dev);
    _DBG_("pci_disable_device");
    pci_disable_device(dev);
    mutex_unlock(&gDriverLock);
    _MSG_("----------------------------");
}

/**
 * 注册驱动节点
 */
static int _zpcidev_miscdev_register(struct miscdevice* md, const char *name, const struct file_operations* ops)
{
    int ret = -1;
    _DBG_("+++");
    do {
        INIT_LIST_HEAD(&md->list);
        md->minor = MISC_DYNAMIC_MINOR;
        md->name = name;
        md->fops = ops;
        md->mode = S_IRWXU | S_IRWXG | S_IRWXO;
        ret = misc_register(md);
        if (ret) {
            _ERR_("misc_register failed");
            break;
        }
    } while (0);
    _DBG_("---dev:'%s' ret=%d---", name, ret);
    return ret;
}



int pcidev_manage_miscdev_register(zpcidev_device *pcidev)
{
    int ret = 0, i;
    board_node_t *curBoardNode;
    board_node_t *nodeHeader = &gDriverRes.manageDev.nodeHeader;
    _DBG_("+++");
    do {
        if (!gDriverRes.manageDev.devIsReady){
            ret = _zpcidev_miscdev_register(&gDriverRes.manageDev.dev, gDriverRes.manageDev.nodeHeader.caDevName, &zpcidev_manage_fops);
            gDriverRes.manageDev.devIsReady = !ret;
        }
        ret = _zpcidev_miscdev_register(&pcidev->miscDev, pcidev->miscDevName, &zpcidev_fops);
        pcidev->miscdev_rdy = !ret;
        curBoardNode = create_board_info(pcidev);
        pcidev->boardNode = curBoardNode;
        curBoardNode->next = nodeHeader;
        curBoardNode->prev = nodeHeader->prev;
        nodeHeader->prev->next = curBoardNode;
        nodeHeader->prev = curBoardNode;

        for (i = 0; i < SEM_SIZE; i++){
            sema_init(&pcidev->semHr[i].sem, 0);
        }
    } while (0);
    _DBG_("--- name='%s', ret=%d", pcidev->miscDevName, ret);
    return ret;
}


static zpcidev_device* _create_zpcidev_device_data(__u32 devIndex){
    zpcidev_device* zpciDev = kzalloc(sizeof(zpcidev_device), GFP_KERNEL);
    if (!zpciDev){
        _ERR_("kzalloc(%u) failed", (unsigned)sizeof(zpcidev_device));
        return NULL;
    }
    _DBG_("kzalloc(%u) succeeded", (unsigned)sizeof(zpcidev_device));
    memset(zpciDev, 0, sizeof(zpcidev_device));
    sprintf(zpciDev->miscDevName, ZPCIDEV_DRIVER_NAME "%d", devIndex);
    _MSG_("device node: /dev/%s", zpciDev->miscDevName);
    zpciDev->devIndex = devIndex;
    return zpciDev;
}

static int zpcidev_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
    int err = -ENODEV, hw;
    zpcidev_device *d = NULL;
    // zpcidev_port *port = NULL;
    __u32 curDeviceIndex = 0;

    mutex_lock(&gDriverLock);

    curDeviceIndex = gDriverRes.deviceNum++;
    _MSG_("+++++++++++++++ dev=0x%p board:%d +++++++++++++++", dev, curDeviceIndex);
    do {
        if (pci_enable_device(dev)) {
            _ERR_("pci_enable_device failed");
            break;
        }
        _DBG_("pci_enable_device succeeded");
        pci_set_master(dev);

        d = _create_zpcidev_device_data(curDeviceIndex);
        if (!d) break;
        pci_set_drvdata(dev, d);

        spin_lock_init(&d->InterruptMsg.locked);
        
        if (pci_request_regions(dev, d->miscDevName)) { //该位置不能使用局部变量
            _ERR_("pci_request_regions failed");
            break;
        }
        _DBG_("pci_request_regions succeeded");
        //检测设备型号
        hw = zpcidev_hw_detect(dev);
        if (hw < 0) {
            _ERR_("zpcidev_hw_detect failed");
            break;
        }
        _MSG_("zpcidev_hw_detect succeeded: hw=%d", hw);

        d->irq = -1;
        d->deviceType = hw;
        d->nChannels = zpcidev_ports[d->deviceType];
        d->pcidev = dev;

        _zpcidev_cdma_init(&d->dmaHandle);

        /* get bar addr */
        zpcidev_map_bars(d);
        /* alloc cdma content */
        _zpcidev_cdma_alloc(d);

        // zpcidev_hw_init(d, 1);

        // ZPCIDEV_MUTEX_INIT(&d->lock);
        // for (i = 0; i < d->nChannels; i++) {
        //     port = &d->ports[i];
        //     port->type = hw;
        //     switch (d->deviceType) {
        //     case PCI_100X:
        //     case PCI_200X:
        //     case ZPCIDEV_HW_TYPES:
        //         // port->base = (u8*)(d->bars[0].base) + 0x10 * i;
        //         break;
        //     }
        // }

        /* 注册中断 */
        _DBG_("irq=%d", dev->irq);
        err = request_irq(dev->irq, (irq_handler_t)zpcidev_isr, IRQF_SHARED, d->miscDevName , d);
        // err = request_irq(dev->irq, (irq_handler_t)zpcidev_isr, IRQF_TRIGGER_RISING | IRQF_SHARED, d->miscDevName , d);
        if (err) {
            _ERR_("request_irq failed: err=%d", err);
            break;
        }
        d->irq = dev->irq;
        err = pcidev_manage_miscdev_register(d);
        if (err) {
            _ERR_("zpcidev_manage_miscdev_register failed: err=%d", err);
            break;
        }

    } while (0);
    mutex_unlock(&gDriverLock);
    if (err) zpcidev_remove(dev);
    _MSG_("------------ dev=0x%p board:%d ret=%d ------------", dev, curDeviceIndex, err);
    return err;
}

static int zpcidev_suspend(struct pci_dev *dev, pm_message_t state)
{
    // zpcidev_device *d = pci_get_drvdata(dev);
    // _DBG_("+++ type=%d", d->type);
    pci_save_state(dev);
    pci_disable_device(dev);
    pci_set_power_state(dev, pci_choose_state(dev, state));
    _DBG_("---");
    return 0;
}
static int zpcidev_resume(struct pci_dev *dev)
{
    zpcidev_device *d = pci_get_drvdata(dev);
    int err;
    // _DBG_("+++ type=%d", d->type);
    pci_set_power_state(dev, PCI_D0);
    pci_restore_state(dev);
    err = pci_enable_device(dev);
    if (!err && d) zpcidev_hw_init(d, 3);
    _DBG_("--- pci_enable_device: err=%d", err);
    return err;
}
static struct pci_driver zpcidev_driver = {
    .name = ZPCIDEV_DRIVER_NAME,
    .id_table = zpcidev_id_table,
    .probe = zpcidev_probe,
    .remove = zpcidev_remove,
    .suspend = zpcidev_suspend,
    .resume = zpcidev_resume,
};
static int zpcidev_module_init(void)
{
    int err;
    _WRN_("+++ driver was compiled on %s at %s +++", __DATE__, __TIME__);
    _WRN_("+++ driver version: %s +++", ZPCIDEV_DRIVER_VERS);
    /* 注册驱动 */
    mutex_lock(&gDriverLock);
    memcpy(gDriverRes.caDriverVersion, ZPCIDEV_DRIVER_VERS, strlen(ZPCIDEV_DRIVER_VERS));
    gDriverRes.deviceNum = 0;
    memcpy(gDriverRes.manageDev.nodeHeader.caDevName, ZPCIDEV_DRIVER_NAME, strlen(ZPCIDEV_DRIVER_NAME));
    gDriverRes.manageDev.nodeHeader.next = gDriverRes.manageDev.nodeHeader.prev = &gDriverRes.manageDev.nodeHeader;
    gDriverRes.manageDev.devIsReady = 0;
    mutex_unlock(&gDriverLock);

    err = pci_register_driver(&zpcidev_driver);

    _WRN_("------------------------------------------------------------\r\n");
    return err;
}

static void zpcidev_module_exit(void)
{
    _WRN_("+++ driver was compiled on %s at %s +++", __DATE__, __TIME__);
    /* 注销驱动 */
    pci_unregister_driver(&zpcidev_driver);
    _WRN_("------------------------------------------------------------\r\n");
}

module_init(zpcidev_module_init);
module_exit(zpcidev_module_exit);
