4、DMA引擎驱动注册与注销:module_init/module_exit、platform_driver_register、dma_async_device_register

DMA引擎在内核中作为一个异步设备存在,其驱动生命周期包含两个核心注册环节:平台驱动注册(与硬件探测绑定)和DMA设备注册(向DMA核心层暴露通道与操作函数)。本章详细拆解从模块入口到设备注销的完整流程,并给出标准代码骨架。

4.1 驱动注册的两层架构

一个完整的DMA引擎驱动需要先后完成两层注册:

注册层级 API函数 作用对象 触发时机
平台驱动层 platform_driver_register() struct platform_driver module_init
DMA设备层 dma_async_device_register() struct dma_device probe() 回调内部

平台驱动层负责与设备树或ACPI匹配,触发probe;DMA设备层负责向内核DMA框架注册通道、中断、回调函数,使dma_request_channel()等上层API可用。

关键原则: 两层注册顺序不可颠倒。必须先注册平台驱动,在probe中完成硬件初始化后再注册DMA设备;注销时顺序相反。

4.2 模块入口与出口:module_init / module_exit

每个DMA引擎驱动都从module_init开始,在module_exit结束。标准模板如下:

static int __init my_dma_init(void)
{
    int ret;

    pr_info("my_dma: initializing driver\n");

    ret = platform_driver_register(&my_dma_platform_driver);
    if (ret)
        pr_err("my_dma: platform driver register failed: %d\n", ret);

    return ret;
}

static void __exit my_dma_exit(void)
{
    platform_driver_unregister(&my_dma_platform_driver);
    pr_info("my_dma: driver unregistered\n");
}

module_init(my_dma_init);
module_exit(my_dma_exit);
避坑提示: 不要在module_init中直接调用dma_async_device_register()。此时硬件尚未探测,DMA寄存器基址、中断号、通道数均未知。必须等到probe()中解析完设备资源后再注册DMA设备。

4.3 platform_driver_register:绑定硬件设备

platform_driver_register()将驱动注册到内核平台总线,与设备树节点或ACPI表匹配。核心数据结构如下:

static struct platform_driver my_dma_platform_driver = {
    .probe  = my_dma_probe,
    .remove = my_dma_remove,
    .driver = {
        .name = "my-dma",
        .of_match_table = my_dma_of_match,
    },
};

匹配成功后,内核调用probe()回调。probe函数需要完成以下步骤:

  1. 获取硬件资源:通过platform_get_resource()获取寄存器基址、IRQ号。
  2. 映射寄存器:使用devm_ioremap_resource()或ioremap()。
  3. 初始化自旋锁与任务队列:保护通道描述符链表。
  4. 填充struct dma_device:设置通道数、capabilities、操作函数指针。
  5. 调用dma_async_device_register():向DMA核心层注册。

4.4 dma_async_device_register:向DMA核心层注册

这是DMA引擎驱动最关键的一步。需要先填充struct dma_device,再调用注册函数:

static int my_dma_probe(struct platform_device *pdev)
{
    struct my_dma_device *dma_dev;
    int ret;

    dma_dev = devm_kzalloc(&pdev->dev, sizeof(*dma_dev), GFP_KERNEL);
    if (!dma_dev)
        return -ENOMEM;

    /* 1. 获取并映射寄存器资源 */
    dma_dev->regs = devm_platform_ioremap_resource(pdev, 0);
    if (IS_ERR(dma_dev->regs))
        return PTR_ERR(dma_dev->regs);

    /* 2. 获取中断 */
    dma_dev->irq = platform_get_irq(pdev, 0);
    if (dma_dev->irq < 0)
        return dma_dev->irq;

    /* 3. 初始化DMA设备结构体 */
    dma_cap_set(DMA_SLAVE, dma_dev->dma_device.cap_mask);
    dma_dev->dma_device.dev = &pdev->dev;
    dma_dev->dma_device.device_alloc_chan_resources = my_dma_alloc_chan;
    dma_dev->dma_device.device_free_chan_resources  = my_dma_free_chan;
    dma_dev->dma_device.device_prep_slave_sg        = my_dma_prep_slave_sg;
    dma_dev->dma_device.device_issue_pending         = my_dma_issue_pending;
    dma_dev->dma_device.device_tx_status             = my_dma_tx_status;
    /* ... 其他回调赋值 ... */

    /* 4. 注册DMA设备 */
    ret = dma_async_device_register(&dma_dev->dma_device);
    if (ret) {
        dev_err(&pdev->dev, "dma_async_device_register failed: %d\n", ret);
        return ret;
    }

    platform_set_drvdata(pdev, dma_dev);
    dev_info(&pdev->dev, "DMA engine registered successfully\n");

    return 0;
}
常见错误: 忘记设置dma_device.dev字段,或cap_mask未正确初始化,会导致dma_async_device_register()返回-EINVAL。务必在注册前检查所有必填回调是否非空。

4.5 注销流程:remove与module_exit

注销顺序与注册完全相反:先注销DMA设备,再注销平台驱动。remove回调中必须调用dma_async_device_unregister():

static int my_dma_remove(struct platform_device *pdev)
{
    struct my_dma_device *dma_dev = platform_get_drvdata(pdev);

    dma_async_device_unregister(&dma_dev->dma_device);

    /* 释放中断、停止DMA引擎硬件 */
    /* ... 硬件复位、中断释放等操作 ... */

    dev_info(&pdev->dev, "DMA engine unregistered\n");
    return 0;
}

在module_exit中只需调用platform_driver_unregister(),内核会自动遍历已绑定的设备并调用每个设备的remove回调。

4.6 完整生命周期SVG流程图

下图展示了从模块加载到卸载的完整注册与注销流程,包含两层注册的时序关系:

DMA引擎驱动注册与注销完整流程 module_init() platform_driver_register() 匹配设备树 → 调用probe() probe() 回调 获取资源 / ioremap 填充 struct dma_device dma_async_device_register() 进入probe module_exit() platform_driver_unregister() remove() → dma_async_device_unregister() 注册顺序约束 ① 先注册平台驱动 ② probe中注册DMA设备 ③ 注销顺序完全相反

4.7 关键数据结构对比

数据结构 所属层 核心字段 注册函数
struct platform_driver 平台总线 probe, remove, driver.name, of_match_table platform_driver_register()
struct dma_device DMA核心 cap_mask, dev, device_alloc_chan_resources, device_prep_slave_sg, device_issue_pending dma_async_device_register()
调试建议: 在probe()中注册DMA设备后,立即检查/sys/class/dma/目录下是否出现新的dma设备节点。若未出现,检查dmesg中的错误信息,通常为回调函数未赋值或cap_mask配置错误。

4.8 本章小结

  • module_init 只做平台驱动注册,不碰DMA设备注册。
  • platform_driver_register 触发probe回调,probe中完成硬件初始化与DMA设备注册。
  • dma_async_device_register 是DMA引擎驱动生效的最终标志,注册后上层才能使用通道。
  • 注销顺序:先dma_async_device_unregister(),再platform_driver_unregister(),不可颠倒。
  • 所有回调函数(alloc/free/prep/issue_pending/tx_status)必须在注册前完成赋值,否则注册失败。