將 DMA 抽象為一個字元設備,在初始化函數中調用 ~~~~ void dma_alloc_writecombine(struct device dev, size_t size, dma_addr_t handle, gfp_t gfp) ~~~~ 函數來分配兩段物理地址連續的空間,一段作為源空間 ...
將 DMA 抽象為一個字元設備,在初始化函數中調用
void *dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp)
函數來分配兩段物理地址連續的空間,一段作為源空間,一段作為目的空間。
然後將物理地址進行 ioremap 供驅動使用,最後調用 register_chrdev 來註冊這個字元設備。
DMA 的 regs:
#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;
};
配置 DMA(通過 ioctl 調用)
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 一下速度就能看出效果來。