蜕变成蝶~Linux设备驱动之DMA
如果不曾相逢 也许 心绪永远不会沉重 如果真的失之交臂 恐怕一生也不得轻松 一个眼神 便足以让心海 掠过飓风
在贫瘠的土地上 更深地懂得风景 一次远行 便足以憔悴了一颗 羸弱的心
每望一眼秋水微澜 便恨不得 泪水盈盈 死怎能不 从容不迫 爱又怎能 无动于衷
只要彼此爱过一次 就是无憾的人生
也许 也许,永远没有那一天 前程如朝霞般绚烂 也许,永远没有那一天
成功如灯火般辉煌 也许,只能是这样 攀援却达不到峰顶 也许,只能是这样 奔流却掀不起波浪
也许,我们能给予你的 只有一颗 饱经沧桑的心 和满脸风霜
也许有些事情早已经写好,也许一颗心早已经注定,只是在骗自己,她还在爱着你~
DMA概述
DMA是一种无需CPU的参加就可以让外设与系统内存之间进行双向数据传输的硬件机制。它可以使系统CPU从实际的I/O数据传输过程中摆脱出来,大大提高系统的吞吐率,并且在传输期间,CPU还可以并发执行其他任务。
DMA与cache的一致性
cache用作CPU针对内存的缓存,避免CPU每一次都要与相对来说慢点的内存交互数据,从而来提高数据的访问速率,而DMA可以用作内存与外设之间传输数据的方式,数据不需要经过CPU周转。
“假设设备驱动程序把一些数据填充到内存缓冲区中,然后立刻命令硬件设备利用DMA传送方式读取该数据。如果DMA访问这些物理RAM内存单元,而相应的硬件高速缓存行的内容还没有写入RAM中,那么硬件设备所读取的至就是内存缓冲区中的旧值。”
alloc_pages(gfp_mask,order) 返回第一个所分配页框描述符的地址,或者如果分配失败则返回NULL。 __get_free_pages(gfp_mask,order) 类似于alloc_pages(),但它返回第一个所分配页的线性地址。如果需要获得线性地址对应的页框号,那么需要调用virt_to_page(addr)宏产生线性地址。
释放函数:
__free_pages(page,order) 这里主要强调page是要释放缓冲区的线性首地址所在的页框号 free_pages(page,order) 这个函数类似于__free_pages(page,order),但是它接收的参数为要释放的第一个页框的线性地址addr
DMA驱动主要数据结构:
1)DMA单个内核缓冲区数据结构:
typedef struct dma_buf_s { int size; /* buffer size:缓冲大小 */ dma_addr_t dma_start; /* starting DMA address :缓冲区起始物理地址*/ int ref; /* number of DMA references 缓冲区起始虚拟地址*/ void *id; /* to identify buffer from outside 标记 */ int write; /* 1: buf to write , 0: buf to read DMA读还是写*/ struct dma_buf_s *next; /* next buf to process 指向下一个缓冲区结构*/ } dma_buf_t;
2)DMA寄存器数据结构:
/* DMA control register structure */ typedef struct { volatile u_long DISRC;/源地址寄存器 volatile u_long DISRCC;//源控制寄存器 volatile u_long DIDST;//目的寄存器 volatile u_long DIDSTC;//目的控制寄存器 volatile u_long DCON;//DMA控制寄存器 volatile u_long DSTAT;//状态寄存器 volatile u_long DCSRC;//当前源 volatile u_long DCDST;//当前目的 volatile u_long DMASKTRIG;//触发掩码寄存器 } dma_regs_t;
3)DMA设备数据结构
/* DMA device structre */ typedef struct { dma_callback_t callback;//DMA操作完成后的回调函数,在中断处理例程中调用 u_long dst;//目的寄存器内容 u_long src;//源寄存器内容 u_long ctl;//此设备的控制寄存器内容 u_long dst_ctl;//目的控制寄存器内容 u_long src_ctl;//源控制寄存器内容 } dma_device_t;
4)DMA通道数据结构
/* DMA channel structure */ typedef struct { dmach_t channel;//通道号:可为0,1,2,3 unsigned int in_use; /* Device is allocated 设备是否已*/ const char *device_id; /* Device name 设备名*/ dma_buf_t *head; /* where to insert buffers 该DMA通道缓冲区链表头*/ dma_buf_t *tail; /* where to remove buffers该DMA通道缓冲区链表尾*/ dma_buf_t *curr; /* buffer currently DMA'ed该DMA通道缓冲区链表中的当前缓冲区*/ unsigned long queue_count; /* number of buffers in the queue 链表中缓冲区个数*/ int active; /* 1 if DMA is actually processing data 该通道是否已经在使用*/ dma_regs_t *regs; /* points to appropriate DMA registers 该通道使用的DMA控制寄存器*/ int irq; /* IRQ used by the channel //通道申请的中断号*/ dma_device_t write; /* to write //执行读操作的DMA设备*/ dma_device_t read; /* to read 执行写操作的DMA设备*/ } s3c2410_dma_t;
DMA驱动主要函数功能分析:
写一个DMA驱动的主要工作包括:DMA通道申请、DMA中断申请、控制寄存器设置、挂入DMA等待队列、清除DMA中断、释放DMA通道.
int s3c2410_request_dma(const char *device_id, dmach_t channel, dma_callback_t write_cb, dma_callback_t read_cb) (s3c2410_dma_queue_buffer);
函数描述:申请某通道的DMA资源,填充s3c2410_dma_t 数据结构的内容,申请DMA中断。
输入参数:device_id DMA 设备名;channel 通道号;
write_cb DMA写操作完成的回调函数;read_cb DMA读操作完成的回调函数
输出参数:若channel通道已使用,出错返回;否则,返回0
int s3c2410_dma_queue_buffer(dmach_t channel, void *buf_id, dma_addr_t data, int size, int write) (s3c2410_dma_stop);
函数描述:这是DMA操作最关键的函数,它完成了一系列动作:分配并初始化一个DMA内核缓冲区控制结构,并将它插入DMA等待队列,设置DMA控制寄存器内容,等待DMA操作触发
输入参数: channel 通道号;buf_id,缓冲区标识
dma_addr_t data DMA数据缓冲区起始物理地址;size DMA数据缓冲区大小;write 是写还是读操作
输出参数:操作成功,返回0;否则,返回错误号
int s3c2410_dma_stop(dmach_t channel)
函数描述:停止DMA操作。
int s3c2410_dma_flush_all(dmach_t channel)
函数描述:释放DMA通道所申请的所有内存资源
void s3c2410_free_dma(dmach_t channel)
函数描述:释放DMA通道
因为各函数功能强大,一个完整的DMA驱动程序中一般只需调用以上3个函数即可。可在驱动初始化中调用s3c2410_request_dma,开始DMA传输前调用s3c2410_dma_queue_buffer,释放驱动模块时调用s3c2410_free_dma。
版权所有,转载请注明转载地址:http://www.cnblogs.com/lihuidashen/p/4470678.html