Chinaunix首页 | 论坛 | 博客
  • 博客访问: 294070
  • 博文数量: 76
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 715
  • 用 户 组: 普通用户
  • 注册时间: 2015-05-20 20:38
文章分类
文章存档

2016年(20)

2015年(56)

分类: 嵌入式

2015-07-03 21:52:21

DMA编程

15年6月23日10:33:06

(一)DMADirect Memory Access:直接内存访问,是一种硬件机制,它允许外围设备和主内存之间直接传输他们的I/O数据,而不需要cpu的参与,使用这种机制可以大大提高与设备通信的吞吐量,免去了大量的计算开销。


DMA方式的数据传输由DMA控制器(DMAC)控制,在传输期间,CPU可以并发地执行其他任务,当DMA结束后,DMAC通过中断通知CPU数据传输完毕,然后由CPU执行相应的中断服务函数进行后处理。


(二)DMAcache的一致性

cache是被CPU用作针对内存的缓存,这时候,如果DMA的目的地址与cache所缓存的内存地址访问有重叠的话,经过DMA的操作,内存里面数据已经被修改,但是cache里面的还是原来的数据,但是CPU不知道,这样就导致了不一致的问题。

解决由于DMA导致的cache一致性的问题,最简单的办法就是直接禁止DMA目标地址范围内内存的cache功能。损失一小部分性能,但是却更可靠。


(三)写DMA驱动的步骤:

  1. 把源告诉DMA

  2. 把目的告诉DMA

  3. size告诉DMA

  4. 设置DMA参数

    4.1 地址 递增还是递减

    4.2 启动DMA的时候,是手工启动还是外部启动

  5. 启动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就可以用。

所以,AD是一一对应的,只不过,A是虚拟地址,而D是物理地址。对任意一个操作都将改变缓冲区内容。


我对此函数的理解是,调用此函数将会分配一段内存,D将返回这段内存的实际物理地址供DMA来使用,A将是D对应的

虚拟地址供操作系统调用,对AD的的任意一个进行操作,都会改变这段内存缓冲区的内容。

*******************************************************************************

这个程序主要的难点就是设置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

阅读(1722) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~