4. 创建GPU内核模块
任务说明
本案例目标是在Linux内核中创建一个可加载的GPU设备驱动框架模块。该模块完成三件事:注册平台驱动、在探测函数中初始化GPU设备的基本资源(寄存器映射、中断号获取)、在移除函数中完成资源释放。模块加载时输出设备信息,卸载时清理所有资源。
内核模块实现
// gpu_drv.c
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#define DRV_NAME "gpu_drv"
struct gpu_device {
void __iomem *reg_base;
int irq;
struct resource *res;
};
static irqreturn_t gpu_irq_handler(int irq, void *dev_id)
{
struct gpu_device *gpu = dev_id;
u32 status;
status = ioread32(gpu->reg_base + 0x00);
dev_info(&platform_get_drvdata(gpu)->dev, "GPU IRQ status=0x%x\n", status);
return IRQ_HANDLED;
}
static int gpu_probe(struct platform_device *pdev)
{
struct gpu_device *gpu;
struct resource *res;
int ret;
gpu = devm_kzalloc(&pdev->dev, sizeof(*gpu), GFP_KERNEL);
if (!gpu)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -EINVAL;
gpu->res = res;
gpu->reg_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(gpu->reg_base))
return PTR_ERR(gpu->reg_base);
gpu->irq = platform_get_irq(pdev, 0);
if (gpu->irq < 0)
return gpu->irq;
ret = devm_request_irq(&pdev->dev, gpu->irq, gpu_irq_handler,
0, DRV_NAME, gpu);
if (ret)
return ret;
platform_set_drvdata(pdev, gpu);
dev_info(&pdev->dev, "GPU probed: reg=0x%pR irq=%d\n", res, gpu->irq);
return 0;
}
static int gpu_remove(struct platform_device *pdev)
{
dev_info(&pdev->dev, "GPU removed\n");
return 0;
}
static const struct of_device_id gpu_of_match[] = {
{ .compatible = "vendor,gpu-core" },
{ }
};
MODULE_DEVICE_TABLE(of, gpu_of_match);
static struct platform_driver gpu_driver = {
.probe = gpu_probe,
.remove = gpu_remove,
.driver = {
.name = DRV_NAME,
.of_match_table = gpu_of_match,
},
};
module_platform_driver(gpu_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("GPU Driver Course");
MODULE_DESCRIPTION("Basic GPU device driver framework");
设备树节点
// gpu.dtsi
gpu@10000000 {
compatible = "vendor,gpu-core";
reg = <0x0 0x10000000 0x0 0x1000>;
interrupts = <0 42 4>;
};
Makefile
obj-m := gpu_drv.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
模块加载与验证
将设备树节点加入系统设备树并编译,或使用以下方式在运行时模拟设备:
# 编译模块
make
# 加载模块(需root)
insmod gpu_drv.ko
# 查看模块加载日志
dmesg | tail -5
# 卸载模块
rmmod gpu_drv
若设备树中已配置对应节点,加载模块后dmesg应显示类似gpu_probe的探测日志。若没有真实硬件,可通过platform_device_register_simple在模块加载时动态注册一个测试设备来验证探测路径。
验证判定标准
insmod后lsmod | grep gpu_drv能看到模块已加载;- 若设备树匹配成功,
dmesg中出现GPU probed日志,且寄存器地址与中断号与设备树一致; rmmod后模块从lsmod中消失,dmesg出现GPU removed;- 中断触发时(若硬件产生中断),
gpu_irq_handler打印状态寄存器值。