linux下的 pci 驱动编程模型

一、概述

        我这里pci 设备是PowerPC2020 和fpga 通过pci 总线进行相连通信。

二、驱动模型:

#include
#include
#include
#include
#include
#include

#include

#define SEND_BUF_SIZE (8192)
#define RECV_BUF_SIZE (131072)
#define MODULE_NAME "interrupt"

/* 指明该驱动程序适用于哪一些PCI设备 */
static const struct pci_device_id pcidevtbl[] = {
{ SW_VENDER_ID, SW_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{},
};
static struct file_operations fops = {
    .owner = THIS_MODULE,
.read  = dev_fifo_read,
.write = dev_fifo_write,
.unlocked_ioctl   = dev_fifo_ioctl,
};

//分配初始化混杂设备对象
static struct miscdevice misc = {
    .minor = MISC_DYNAMIC_MINOR, //自动分配次设备号
    .name = "switchctl-%d", 
    .fops = &fops 

};

void demo_remove(struct pci_dev *dev)
{


return ;
}
static irqreturn_t interrupt(int irq, void *dev_id)
{


return 0;

}

int demo_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* New device inserted */

{

}

static struct pci_driver demo_pci_driver = {
name: DEMO_MODULE_NANE, 
id_table: pcidevtbl, 
probe: demo_probe, 
remove: demo_remove 
};

static int __init pci_init(void)
{
int ret = 0;
ret = misc_register(&misc);
if(ret != 0 )
{
printk("misc_register failed!\n");
return 0;
}

if (!pci_register_driver(&demo_pci_driver)) {
pci_unregister_driver(&demo_pci_driver);
return -ENODEV;
}
return 0;
}
static void __exit pci_exit(void)
{
pci_unregister_driver(&demo_pci_driver);
misc_deregister(&misc);
return ;
}
module_init(pci_init);
module_exit(pci_exit);

MODULE_LICENSE("GPL");

三、代码分析:

    1、简单的混杂设备驱动模型不必赘述。

    2、主要分析static struct pci_driver demo_pci_driver;结构体。

这个结构体中定义了pci 从设备的deviceid device name 等等一些信息,这些信息很重要,如果填错在pci 注册的的时候会报错,当然也不会进入对应的probe 函数中去。

3、进入pci 注册成功后的probe 中后会有一些模型化的步骤。具体我们如下分析。

四、pci probe 分析

int demo_probe(struct pci_dev *pdev, const struct pci_device_id *id) /* New device inserted */
{
int err;

/* 启动PCI设备 */
if (pci_enable_device(pdev))
return -EIO;
/* 设备DMA标识 */
if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
return -ENODEV;
}
err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
if (err) {
printk(KERN_ERR "no suitable DMA configuring, aborting\n");
goto fail_no_dma;
}
err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
if (err) {
printk(KERN_ERR "no suitable DMA configuring, aborting\n");
goto fail_no_dma;

}

        / *这三个函数很重要,一下可以获取和pci 从设备通信的物理地址和长度, 当然这个物理地址不能直接使用需要做一下映射* /

bar0_s = pci_resource_start(pdev, 0);
        bar0_e = pci_resource_end(pdev, 0);
        bar0_l = pci_resource_len(pdev, 0);
virt_addr = ioremap(bar0_s, bar0_l);

printk("virt_addr = 0x%p\n", virt_addr);

/*至于是否要启动和中断相关的msi这个要读取从设备的datesheet 或则fpga 编程的时候是否打开这个msi 功能 */

err = pci_enable_msi_block(pdev, 1);

        if (err != 0) {

printk("MSI enable failed\n");
goto fail_enable_msi;

}

        /*和pci 相关的中断这里注册一下*/

        err = request_irq(pdev->irq, crrc_interrupt,
IRQF_SHARED, MODULE_NAME, pdev);
if (!err) {
printk("request_irq:%d\n", pdev->irq);
} else {
printk("request_irq:%d failed\n", pdev->irq);
goto fail_req_irq;

}

        pci_set_master(pdev);

/*因为我和fpga 是网卡的mac 层的角色,所以这里涉及到dma 上下行的通信,所以这里就有一个dma 映射的函数,这里以pci从设备实际情况来确定是否使用如下dma 功能*/

        recv_buf = pci_alloc_consistent(pdev, RECV_BUF_SIZE, &recv_buf_hw);
if (recv_buf == NULL) {
printk("dma recv buffer alloc failed.\n");
err = -ENOMEM;
goto fail_alloc_recv;
} else {
printk("dma recv buffer alloc success.\n");
memset(recv_buf, 0, RECV_BUF_SIZE);
printk("recv_buf_hw = 0x%x\n", recv_buf_hw);
printk("RECV_BUF_SIZE = 0x%x\n", RECV_BUF_SIZE);
printk("recv_buf = 0x%p\n", recv_buf);
}


send_buf = pci_alloc_consistent(pdev, SEND_BUF_SIZE, &send_buf_hw);
if (send_buf == NULL) {
printk("dma send buffer alloc failed\n");
err = -ENOMEM;
goto fail_alloc_send;
} else {
printk("dma send buffer alloc success.\n");
memset(send_buf, 0, SEND_BUF_SIZE);
printk("send_buf_hw = 0x%x\n", send_buf_hw);
printk("SEND_BUF_SIZE = 0x%x\n", SEND_BUF_SIZE);
printk("send_buf = 0x%p\n", send_buf);
}

你可能感兴趣的:(linux编程)