PCIe驱动开发(2)— 第一个简单驱动编写和测试

PCIe驱动开发(2)--- 第一个简单驱动编写和测试

一、前言

教程参考:02_实战部分_PCIE设备测试

教程参考:03_PCIe设备驱动源码解析

二、驱动编写

新建hello_pcie.c文件

bash 复制代码
touch hello_pcie.c

然后编写内容如下所示:

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文件编写内容如下:

bash 复制代码
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文件

四、驱动加载及测试

驱动编译完成后使用如下命令加载即可:

bash 复制代码
sudo insmod hello_pcie.ko

然后使用lspci查看该pcie设备,可以看到驱动加载成功:

同时我们也可以看到其BAR0基地址为0xfea00000,我们使用devmem向其0x08编译地址写入数据进行阶乘运算:

使用详细说明可以查看查看qemu源码的docs/specs/edu.txt文件

然后我们使用dmesg命令可以查看驱动的相关打印:

相关推荐
Y1anoohh1 小时前
驱动学习专栏--字符设备驱动篇--2_字符设备注册与注销
linux·c语言·驱动开发·学习
Despacito0o7 小时前
FreeRTOS教程 - 任务间通信:使用队列(Queue)
驱动开发·stm32·单片机·嵌入式硬件·51单片机·freertos·嵌入式实时数据库
程序员JerrySUN8 小时前
驱动开发硬核特训 · Day 13:从 device_create 到 sysfs,设备文件是如何生成的?
驱动开发
菜狗想要变强1 天前
RVOS-7.实现抢占式多任务
linux·c语言·驱动开发·单片机·嵌入式硬件·risc-v
程序员JerrySUN1 天前
驱动开发硬核特训 · USB 摄像头专题:原理 + 实战深度解析(基于 linux-imx)
linux·数据库·驱动开发
xu_wenming3 天前
FlashDB 在嵌入式系统中占用硬件资源
c语言·数据库·驱动开发·嵌入式硬件
xxxx1234453 天前
Linux驱动开发-网络设备驱动
linux·运维·驱动开发
程序员JerrySUN3 天前
驱动开发硬核特训 · Day 10 (理论上篇):设备模型 ≈ 运行时的适配器机制
驱动开发
程序员JerrySUN3 天前
驱动开发硬核特训 · Day 6 : 深入解析设备模型的数据流与匹配机制 —— 以 i.MX8M 与树莓派为例的实战对比
驱动开发
SunshineBooming4 天前
DirectX12 - 基本知识 - 图形驱动的本质
c++·驱动开发