一、概述
我这里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);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);
}