分类: 嵌入式
2015-07-03 21:52:21
DMA编程
15年6月23日10:33:06
(一)DMA(Direct Memory Access):直接内存访问,是一种硬件机制,它允许外围设备和主内存之间直接传输他们的I/O数据,而不需要cpu的参与,使用这种机制可以大大提高与设备通信的吞吐量,免去了大量的计算开销。
DMA方式的数据传输由DMA控制器(DMAC)控制,在传输期间,CPU可以并发地执行其他任务,当DMA结束后,DMAC通过中断通知CPU数据传输完毕,然后由CPU执行相应的中断服务函数进行后处理。
(二)DMA与cache的一致性
cache是被CPU用作针对内存的缓存,这时候,如果DMA的目的地址与cache所缓存的内存地址访问有重叠的话,经过DMA的操作,内存里面数据已经被修改,但是cache里面的还是原来的数据,但是CPU不知道,这样就导致了不一致的问题。
解决由于DMA导致的cache一致性的问题,最简单的办法就是直接禁止DMA目标地址范围内内存的cache功能。损失一小部分性能,但是却更可靠。
(三)写DMA驱动的步骤:
把源告诉DMA
把目的告诉DMA
把size告诉DMA
设置DMA参数
4.1 地址 递增还是递减
4.2 启动DMA的时候,是手工启动还是外部启动
启动DMA
分配DMA缓冲区的时候,不能够使用kmalloc,因为使用kmalloc分配的缓冲区在物理地址上不一定是连续的。
应该使用 dma_alloc_coherent函数,函数原型如下:
void *dma_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *handle, gfp_t gfp)
前两个参数是device结构和所需缓冲区的大小,第三个参数是返回的物理内存地址,DMA就可以用,第四个参数是GFP标识,通常为GFP_KERNEL。这个函数的返回值是申请到的DMA缓冲区的虚拟地址。
而通常的用法是分配一个写合并的DMA缓冲区:
void *dma_alloc_writecombine(struct device *dev, size_t size,
dma_addr_t *handle, gfp_t gfp)
函数的参数与上者相同。
这两个函数的释放函数分别是
void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t handle);
void dma_free_writecombine(struct device *dev, size_t size, void *cpu_addr, dma_addr_t handle)
网上看的一个对于这个函数讲的不错的,粘贴在这:
*****************************************************************************
大家都知道,DMA的操作是需要物理地址的,但是在linux内核中使用的都是虚拟地址,如果我们想要用DMA对一段内存进行操作,我们如何得到这一段内存的物理地址和虚拟地址的映射呢?dma_alloc_coherent这个函数实现了这种机制。
1、函数原型:void *dma_alloc_coherent(struct device *dev, size_t size,dma_addr_t *dma_handle,gfp_t gfp);
2、调用
A = dma_alloc_writecombine(B,C,D,GFP_KERNEL);
含义:
A: 内存的虚拟起始地址,在内核要用此地址来操作所分配的内存
B: struct device指针,可以平台初始化里指定,主要是dma_mask之类,可参考framebuffer
C: 实际分配大小,传入dma_map_size即可
D: 返回的内存物理地址,dma就可以用。
所以,A和D是一一对应的,只不过,A是虚拟地址,而D是物理地址。对任意一个操作都将改变缓冲区内容。
我对此函数的理解是,调用此函数将会分配一段内存,D将返回这段内存的实际物理地址供DMA来使用,A将是D对应的
虚拟地址供操作系统调用,对A和D的的任意一个进行操作,都会改变这段内存缓冲区的内容。
*******************************************************************************
这个程序主要的难点就是设置DMA寄存器了,其他没有什么难点,把程序粘贴如下:
1
2 #include <linux/module.h>
3 #include <linux/kernel.h>
4 #include <linux/fs.h>
5 #include <linux/init.h>
6 #include <linux/delay.h>
7 #include <linux/irq.h>
8 #include <asm/uaccess.h>
9 #include <asm/irq.h>
10 #include <asm/io.h>
11 #include <asm/arch/regs-gpio.h>
12 #include <asm/hardware.h>
13 #include <linux/poll.h>
14 #include <linux/dma-mapping.h>
15
16 #define MEM_CPY_NO_DMA 0
17 #define MEM_CPY_DMA 1
18
19 #define DMA_SIZE (512*1024)
20
21 #define DMA0_BASE_ADDR 0x4B000000
22 #define DMA1_BASE_ADDR 0x4B000040
23 #define DMA2_BASE_ADDR 0x4B000080
24 #define DMA3_BASE_ADDR 0x4B0000C0
25
26 struct s3c_dma_regs {
27 unsigned long disrc;
28 unsigned long disrcc;
29 unsigned long didst;
30 unsigned long didstc;
31 unsigned long dcon;
32 unsigned long dstat;
33 unsigned long dcsrc;
34 unsigned long dcdst;
35 unsigned long dmasktrig;
36 };
37
38 static int major = 0;
39
40 static char *src;
41 static u32 src_phys;
42
43 static char *dst;
44 static u32 dst_phys;
45
46 static struct class *cls;
47
48 static volatile struct s3c_dma_regs *dma_regs;
49
50 static DECLARE_WAIT_QUEUE_HEAD(dma_waitq);
51 /* interrupt flag */
52 static volatile int ev_dma = 0;
53
54 static int s3c_dma_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
55 {
56 int i;
57
58 memset(src, 0xAA, DMA_SIZE);
59 memset(dst, 0x55, DMA_SIZE);
60
61 switch (cmd)
62 {
63 case MEM_CPY_NO_DMA :
64 {
65 for (i = 0; i < DMA_SIZE; i++)
66 {
67 dst[i] = src[i];
68 }
69
70 if (memcmp(src, dst, DMA_SIZE) == 0)
71 {
72 printk("MEM_CPY_NO_DMA OK!!\n");
73 }
74 else
75 {
76 printk("MEM_CPY_NO_DMA ERROR!!\n");
77 }
78 break;
79 }
80
81 case MEM_CPY_DMA :
82 {
83 ev_dma = 0;
84 /* tell DMA the rcs dst and size */
85 dma_regs->disrc = src_phys; /* the physic address of src */
86 dma_regs->disrcc = (0<<1) | (0<<0); /* src located in AHB, and increase src addr*/
87 dma_regs->didst = dst_phys; /* the physic address of dst */
88 dma_regs->didstc = (0<<2) | (0<<1) | (0<<0); /* dst located in AHB,and increase dst addr */
89 dma_regs->dcon = (1<<30)|(1<<29)|(0<<28)|(1<<27)|(0<<23)|(0<<20)|(DMA_SIZE<<0); /* enable irq,singal transfer */
90
91 /* start DMA */
92 dma_regs->dmasktrig = (1<<1) | (1<<0);
93
94 /* sleep */
95 wait_event_interruptible(dma_waitq, ev_dma);
96
97 if (memcmp(src, dst, DMA_SIZE) == 0)
98 {
99 printk("MEM_CPY_DMA OK!!\n");
100 }
101 else
102 {
103 printk("MEM_CPY_DMA ERROR!!\n");
104 }
105
106 break;
107 }
108 }
109
110 return 0;
111 }
112
113 static struct file_operations dma_fops = {
114 .owner = THIS_MODULE,
115 .ioctl = s3c_dma_ioctl,
116 };
117
118 static irqreturn_t s3c_dma_irq(int irq, void *devid)
119 {
120 /* wake up */
121 ev_dma = 1;
122 wake_up_interruptible(&dma_waitq);
123
124 return IRQ_HANDLED;
125 }
126
127 static int s3c_dma_init(void)
128 {
129 if (request_irq(IRQ_DMA3, s3c_dma_irq, 0, "s3c_dma", 1))
130 {
131 printk("can't request_irq for DMA\n");
132 return -EBUSY;
133 }
134
135 /* distribute src/dst buff */
136 src = dma_alloc_writecombine(NULL, DMA_SIZE, &src_phys, GFP_KERNEL);
137 if (src == NULL)
138 {
139 printk("can not alloc buff for src!\n");
140 free_irq(IRQ_DMA3, 1);
141 return -ENOMEM;
142 }
143
144 dst = dma_alloc_writecombine(NULL, DMA_SIZE, &dst_phys, GFP_KERNEL);
145 if (dst == NULL)
146 {
147 dma_free_writecombine(NULL, DMA_SIZE, src, src_phys);
148 printk("can not alloc buff for dst!\n");
149 free_irq(IRQ_DMA3, 1);
150 return -ENOMEM;
151 }
152
153 major = register_chrdev(0, "s3c_dma", &dma_fops);
154
155 cls = class_create(THIS_MODULE, "s3c_dma");
156 class_device_create(cls, NULL, MKDEV(major, 0), NULL, "dma"); // /dev/dma
157
158 dma_regs = ioremap(DMA3_BASE_ADDR, sizeof(struct s3c_dma_regs));
159 return 0;
160 }
161
162 static void s3c_dma_exit(void)
163 {
164 iounmap(dma_regs);
165 class_device_destroy(cls, MKDEV(major, 0));
166 class_destroy(cls);
167 unregister_chrdev(major, "s3c_dma");
168 dma_free_writecombine(NULL, DMA_SIZE, src, src_phys);
169 dma_free_writecombine(NULL, DMA_SIZE, dst, dst_phys);
170 free_irq(IRQ_DMA3, 1);
171 }
172
173 module_init(s3c_dma_init);
174 module_exit(s3c_dma_exit);
175
176 MODULE_LICENSE("GPL");
177