心理学哲学批判性思维 2018-03-12
将 DMA 抽象为一个字符设备,在初始化函数中调用
void *dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp)
函数来分配两段物理地址连续的空间,一段作为源空间,一段作为目的空间。
然后将物理地址进行 ioremap 供驱动使用,最后调用 register_chrdev 来注册这个字符设备。
#define DMA0_BASE_ADDR 0x4B000000 #define DMA1_BASE_ADDR 0x4B000040 #define DMA2_BASE_ADDR 0x4B000080 #define DMA3_BASE_ADDR 0x4B0000C0 struct s3c_dma_regs { unsigned long disrc; unsigned long disrcc; unsigned long didst; unsigned long didstc; unsigned long dcon; unsigned long dstat; unsigned long dcsrc; unsigned long dcdst; unsigned long dmasktrig; };
ev_dma = 0; /* 把源,目的,长度告诉 DMA */ dma_regs->disrc = src_phys; /* 源的物理地址 */ dma_regs->disrcc = (0<<1) | (0<<0); /* 源位于 AHB 总线, 源地址递增 */ dma_regs->didst = dst_phys; /* 目的的物理地址 */ dma_regs->didstc = (0<<2) | (0<<1) | (0<<0); /* 目的位于 AHB 总线, 目的地址递增 */ dma_regs->dcon = (1<<30)|(1<<29)|(0<<28)|(1<<27)|(0<<23)|(0<<20)|(BUF_SIZE<<0); /* 使能中断,单个传输,软件触发, */ /* 启动 DMA */ dma_regs->dmasktrig = (1<<1) | (1<<0); /* 如何知道 DMA 什么时候完成 ? */ /* 休眠 */ wait_event_interruptible(dma_waitq, ev_dma); if (memcmp(src, dst, BUF_SIZE) == 0) { printk("MEM_CPY_DMA OK\n"); } else { printk("MEM_CPY_DMA ERROR\n"); }
当 DMA 开始工作时会休眠一段时间,直到复制完成后触发中断来唤醒。
static irqreturn_t s3c_dma_irq(int irq, void *devid) { /* 唤醒 */ ev_dma = 1; wake_up_interruptible(&dma_waitq); /* 唤醒休眠的进程 */ return IRQ_HANDLED; }
可能这样的实验并不会看出 DMA 的作用,我们可以与普通的复制做一下速度上的对比。例如用 memcpy 来与 DMA PK 一下速度就能看出效果来。