1

Linux内核下DMA相关函数运用​

 2 years ago
source link: https://blog.51cto.com/u_11822586/5623860
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

一、DMA 介绍(直接存储器访问)

DMA(Direct Memory Access,直接内存存取) 是所有现代电脑的重要特色,它允许不同速度的硬件装置来沟通,而不需要依赖于 CPU 的大量中断负载。否则,CPU 需要从来源把每一片段的资料复制到暂存器,然后把它们再次写回到新的地方。在这个时间中,CPU 对于其他的工作来说就无法使用。​

DMA 传输将数据从一个地址空间复制到另外一个地址空间。当CPU 初始化这个传输动作,传输动作本身是由 DMA 控制器来实行和完成。典型的例子就是移动一个外部内存的区块到芯片内部更快的内存区。像是这样的操作并没有让处理器工作拖延,反而可以被重新排程去处理其他的工作。DMA 传输对于高效能 嵌入式系统算法和网络是很重要的。​

在实现DMA传输时,是由DMA控制器直接掌管总线,因此,存在着一个总线控制权转移问题。即DMA传输前,CPU要把总线控制权交给DMA控制器,而在结束DMA传输后,DMA控制器应立即把总线控制权再交回给CPU。一个完整的DMA传输过程必须经过DMA请求、DMA响应、DMA传输、DMA结束4个步骤。​

  1. 请求​CPU对DMA控制器初始化,并向I/O接口发出操作命令,I/O接口提出DMA请求。​
  2. 响应​DMA控制器对DMA请求判别优先级及屏蔽,向总线裁决逻辑提出总线请求。当CPU执行完当前总线周期即可释放总线控制权。此时,总线裁决逻辑输出总线应答,表示DMA已经响应,通过DMA控制器通知I/O接口开始DMA传输。​
  3. 传输​DMA控制器获得总线控制权后,CPU即刻挂起或只执行内部操作,由DMA控制器输出读写命令,直接控制RAM与I/O接口进行DMA传输。​
    在DMA控制器的控制下,在存储器和外部设备之间直接进行数据传送,在传送过程中不需要中央处理器的参与。开始时需提供要传送的数据的起始位置和数据长度。​

当完成规定的成批数据传送后,DMA控制器即释放总线控制权,并向I/O接口发出结束信号。当I/O接口收到结束信号后,一方面停 止I/O设备的工作,另一方面向CPU提出中断请求,使CPU从不介入的状态解脱,并执行一段检查本次DMA传输操作正确性的代码。最后,带着本次操作结果及状态继续执行原来的程序。​

由此可见,DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为RAM与I/O设备开辟一条直接传送数据的通路,使CPU的效率大为提高。​

二、Linux下内核DMA相关函数介绍

2.1 分配一块物理地址连续的内存

static inline void *dma_alloc_writecombine(struct device *dev, size_t size,dma_addr_t *dma_handle, gfp_t flag)​

  • 功能介绍:分配一块物理地址连续的内存,供后续DMA传输使用。​
  • 函数参数:struct device *dev : 传入设备指针,没有填NULL。
    size_t size : 分配的空间大小。
    dma_addr_t *dma_handle :存放分配之后的物理地址。
    gfp_t flag :分配的属性。常用: GFP_KERNEL
  • 返回值: 物理地址对应的虚拟地址,内核代码本身只能操作虚拟地址。​

2.2 释放分配的内存

static inline void dma_free_writecombine(struct device *dev, size_t size,void *cpu_addr, dma_addr_t dma_handle)​

  • 功能介绍:释放之前申请的内存空间。​
  • 函数参数:​

struct device *dev : 传入设备指针,没有填NULL。​

size_t size : 释放空间的大小。​

dma_addr_t *dma_handle :空间的虚拟地址(dma_alloc_writecombine返回值)。​

dma_addr_t dma_handle :空间物理地址​

2.3 分配内存示例​

#include <linux/kernel.h>​

#include <linux/module.h>​

#include <linux/fs.h>​

#include <linux/uaccess.h>​

#include <linux/io.h>​

#include <linux/dma-mapping.h>​

#define BUF_SIZE (512*1024)​

static u32 src_phys; 存放分配的物理起始地址​

static unsigned char *src; //存放分配的虚拟起始地址​

static u8 buff[]="分配一块连续的物理地址,可供DMA使用";​

static int __init tiny4412_dma_dev_init(void) ​

/*分配一块连续的物理地址,可供DMA使用*/​

src=dma_alloc_writecombine(NULL,BUF_SIZE,&src_phys,GFP_KERNEL);​

if(src==NULL)​

printk("空间分配失败!\n");​

else​

printk("驱动安装成功!\n"); /*提示语句*/​

printk("分配的物理地址:0x%X\n",&src_phys);​

printk("分配的虚拟地址:0x%X\n",src);​

memcpy(src,buff,sizeof(buff));​

说明: Tiny4412开发板内存条大小为1G​

return 0;​

static void __exit tiny4412_dma_dev_exit(void) ​

if(src)​

printk("buff=%s\n",buff);​

/*释放分配的空间*/​

dma_free_writecombine(NULL,BUF_SIZE,src,src_phys);​

printk("驱动卸载成功!\n");​

module_init(tiny4412_dma_dev_init);​

module_exit(tiny4412_dma_dev_exit);​

MODULE_LICENSE("GPL");​

MODULE_AUTHOR("tiny4412 wbyq");​


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK