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打印状态寄存器值。
GPU内核模块加载与设备绑定流程 insmod gpu_drv.ko module_platform_driver of_match_table 匹配 gpu_probe() ① devm_kzalloc 分配设备结构 ② platform_get_resource 获取寄存器 ③ devm_ioremap_resource 映射寄存器 ④ platform_get_irq + request_irq 中断触发 gpu_irq_handler() ioread32 读取状态寄存器 rmmod → gpu_remove() devm_* 资源在设备移除时自动释放