PCIe驱动开发(2)— 第一个简单驱动编写和测试
一、前言
教程参考:02_实战部分_PCIE设备测试
教程参考:03_PCIe设备驱动源码解析
二、驱动编写
新建hello_pcie.c
文件
touch hello_pcie.c
然后编写内容如下所示:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#define HELLO_PCI_DEVICE_ID 0x11e8
#define HELLO_PCI_VENDOR_ID 0x1234
#define HELLO_PCI_REVISION_ID 0x10
static struct pci_device_id ids[] = {
{ PCI_DEVICE(HELLO_PCI_VENDOR_ID, HELLO_PCI_DEVICE_ID), },
{ 0 , }
};
static struct hello_pci_info_t {
struct pci_dev *dev;
void __iomem *address_bar0;
} hello_pci_info;
MODULE_DEVICE_TABLE(pci, ids);
static irqreturn_t hello_pci_irq_handler(int irq, void *dev_info)
{
struct hello_pci_info_t *_pci_info = dev_info;
uint32_t irq_status;
// get irq_stutas
irq_status = *((uint32_t *)(_pci_info->address_bar0 + 0x24));
printk("hello_pcie: get irq status: 0x%0x\n", irq_status);
// clean irq
*((uint32_t *)(_pci_info->address_bar0 + 0x64)) = irq_status;
// get irq_stutas
irq_status = *((uint32_t *)(_pci_info->address_bar0 + 0x24));
if(irq_status == 0x00){
printk("hello_pcie: receive irq and clean success. \n");
return IRQ_HANDLED;
}else{
printk("hello_pcie: receive irq but clean failed !!! \n");
return IRQ_NONE;
}
}
static int hello_pcie_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
int bar = 0;
int ret;
resource_size_t len;
ret = pci_enable_device(dev);
if(ret) {
return ret;
}
len = pci_resource_len(dev, bar);
hello_pci_info.address_bar0 = pci_iomap(dev, bar, len);
hello_pci_info.dev = dev;
// register interrupt
ret = request_irq(dev->irq, hello_pci_irq_handler, IRQF_SHARED, "hello_pci", &hello_pci_info);
if(ret) {
printk("request IRQ failed.\n");
return ret;
}
// enable irq for finishing factorial computation
*((uint32_t *)(hello_pci_info.address_bar0 + 0x20)) = 0x80;
return 0;
}
static void hello_pcie_remove(struct pci_dev *dev)
{
// disable irq for finishing factorial computation
*((uint32_t *)(hello_pci_info.address_bar0 + 0x20)) = 0x01;
free_irq(dev->irq, &hello_pci_info);
pci_iounmap(dev, hello_pci_info.address_bar0);
pci_disable_device(dev);
}
static struct pci_driver hello_pci_driver = {
.name = "hello_pcie",
.id_table = ids,
.probe = hello_pcie_probe,
.remove = hello_pcie_remove,
};
static int __init hello_pci_init(void)
{
return pci_register_driver(&hello_pci_driver);
}
static void __exit hello_pci_exit(void)
{
pci_unregister_driver(&hello_pci_driver);
}
MODULE_LICENSE("GPL");
module_init(hello_pci_init);
module_exit(hello_pci_exit);
三、驱动编译
新建Makefile
文件编写内容如下:
ifeq ($(KERNELRELEASE),)
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* modules*
.PHONY: all clean
else
obj-m := hello_pcie.o
endif
然后执行 make
命令进行编译
编译完成后可以得到驱动对应的 .ko文件
四、驱动加载及测试
驱动编译完成后使用如下命令加载即可:
sudo insmod hello_pcie.ko
然后使用lspci查看该pcie设备,可以看到驱动加载成功:
同时我们也可以看到其BAR0基地址为0xfea00000
,我们使用devmem向其0x08编译地址写入数据进行阶乘运算:
使用详细说明可以查看查看qemu源码的docs/specs/edu.txt
文件
然后我们使用dmesg
命令可以查看驱动的相关打印: