Linux dma的使用与理解

1 概述

        本文描述Linux下的dma子系统,包括上层驱动调用方式(consumer),硬件驱动接入(provider)和dma子系统框架。

 2 Linux下dma基本软件框架

        如下框架大致描述了Linux下的dma子系统软件层次关系,Linux内核提供了dma子系统,向上驱动层提供DMA consumer,驱动开发者只需调用从summer的接口传入对应的dma通道需求信息;向下硬件层提供DMA Provider,该层主要由芯片厂商实现,向dma子系统注册一个chan_device;DMA核心层包括dmaengine、virt-dma和of-dma。

3 dma寄存器配置回顾

(1)配置前准备

        1)时钟使能

        启用DMA控制器的时钟(如STM32中通过RCC_AHBPeriphClockCmd开启DMA1/DMA2时钟)14。

        2)参数确定

        传输方向:外设↔存储器或存储器↔存储器18。

        数据量:设置单次传输的数据单元数量(如DMA_CNDTRx寄存器)14。

        数据宽度:选择字节(8位)、半字(16位)或字(32位)48。

(2)寄存器配置关键步骤

        1)地址设置

        外设地址:指向外设数据寄存器(如&USART1->DR)14。

        存储器地址:指向内存缓冲区(如数组SendBuff[100]() )14。

        地址递增模式:外设地址通常固定,存储器地址按数据宽度递增18。

        2)传输模式与优先级

        选择循环模式(自动重载数据量)或单次模式(需手动重启)18。

        设置通道优先级(高/中/低),冲突时按通道编号仲裁14。

        3)中断配置(可选)

        使能传输完成/半传输/错误中断,并绑定中断服务函数14。

(3)启动与调试

        1)初始化DMA通道

               调用初始化函数(如DMA_Init()),加载配置到工作寄存器48。

        2)启动传输

                使能DMA通道(如DMA_Cmd(DMA1_Channel4, ENABLE))14。

        3)数据监控

                通过中断标志位或查询寄存器状态(如DMA_ISR)确认传输完成

4 驱动层调用DMA子系统

        上层驱动使用DMA子系统,如下是deepseek生成的一个内存到内存的过程示例

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/platform_device.h>

#define DEVICE_NAME "dma_example"
#define BUFFER_SIZE 1024
static int major;
static struct dma_chan *dma_chan;
static struct completion dma_complete;
static dma_addr_t src_dma_addr, dst_dma_addr;
static void *src_buffer, *dst_buffer;
// DMA传输完成回调函数
static void dma_callback(void *data)
{
    complete(&dma_complete);
}
// 字符设备的open方法
static int dma_example_open(struct inode *inode, struct file *filp)
{
    return 0;
}
// 字符设备的release方法
static int dma_example_release(struct inode *inode, struct file *filp)
{
    return 0;
}
// 字符设备的write方法,触发DMA传输
static ssize_t dma_example_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
    struct dma_async_tx_descriptor *desc;
    dma_cookie_t cookie;
    // 初始化完成量
    init_completion(&dma_complete);
    // 准备DMA描述符
    desc = dmaengine_prep_dma_memcpy(dma_chan, dst_dma_addr, src_dma_addr, BUFFER_SIZE, DMA_PREP_INTERRUPT);
    if (!desc) {
        printk(KERN_ERR "Failed to prepare DMA descriptor\n");
        return -ENOMEM;
    }
    // 设置回调函数
    desc->callback = dma_callback;
    desc->callback_param = NULL;
    // 提交DMA传输
    cookie = dmaengine_submit(desc);
    if (dma_submit_error(cookie)) {
        printk(KERN_ERR "Failed to submit DMA transfer\n");
        return -EFAULT;
    }
    // 启动DMA传输
    dma_async_issue_pending(dma_chan);
    // 等待DMA传输完成
    wait_for_completion(&dma_complete);
    return count;
}
// 字符设备的文件操作结构体
static struct file_operations fops = {
    .owner = THIS_MODULE,
    .open = dma_example_open,
    .release = dma_example_release,
    .write = dma_example_write,
};
// 驱动模块初始化函数
static int __init dma_example_init(void)
{
    // 注册字符设备
    major = register_chrdev(0, DEVICE_NAME, &fops);
    if (major < 0) {
        printk(KERN_ALERT "Failed to register character device\n");
        return major;
    }
    printk(KERN_INFO "DMA example driver registered with major number %d\n", major);
    // 分配DMA通道
    dma_chan = dma_request_chan(NULL, "memcpy");
    if (!dma_chan) {
        printk(KERN_ERR "Failed to request DMA channel\n");
        unregister_chrdev(major, DEVICE_NAME);
        return -ENODEV;
    }
    // 分配源和目的缓冲区
    src_buffer = dma_alloc_coherent(NULL, BUFFER_SIZE, &src_dma_addr, GFP_KERNEL);
    dst_buffer = dma_alloc_coherent(NULL, BUFFER_SIZE, &dst_dma_addr, GFP_KERNEL);
    if (!src_buffer || !dst_buffer) {
        printk(KERN_ERR "Failed to allocate DMA buffers\n");
        dma_release_channel(dma_chan);
        unregister_chrdev(major, DEVICE_NAME);
        return -ENOMEM;
    }
    // 初始化源缓冲区
    memset(src_buffer, 0xAA, BUFFER_SIZE);
    return 0;
}
// 驱动模块退出函数
static void __exit dma_example_exit(void)
{
    // 释放DMA缓冲区
    dma_free_coherent(NULL, BUFFER_SIZE, src_buffer, src_dma_addr);
    dma_free_coherent(NULL, BUFFER_SIZE, dst_buffer, dst_dma_addr);
    // 释放DMA通道
    dma_release_channel(dma_chan);
    // 注销字符设备
    unregister_chrdev(major, DEVICE_NAME);
    printk(KERN_INFO "DMA example driver unregistered\n");
}
module_init(dma_example_init);
module_exit(dma_example_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple DMA example driver");

        先请求一个dma通道(示例请求了memcpy名的通道),使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值