一個簡單的使用DMA 例子
示例:下面是一個簡單的使用DMA進行傳輸的驅動程序,它是一個假想的設備,只列出DMA相關的部分來說明驅動程序中如何使用DMA的。
函數dad_transfer是設置DMA對內存buffer的傳輸操作函數,它使用流式映射將buffer的虛擬地址轉換到物理地址,設置好DMA控制器,然后開始傳輸數據。
int dad_transfer(struct dad_dev *dev, int write, void *buffer, size_t count) { dma_addr_t bus_addr; unsigned long flags; /* Map the buffer for DMA */ dev->dma_dir = (write ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); dev->dma_size = count; //流式映射,將buffer的虛擬地址轉化成物理地址 bus_addr = pci_map_single(dev->pci_dev, buffer, count, dev->dma_dir); dev->dma_addr = bus_addr; //DMA傳送的buffer物理地址 //將操作控制寫入到DMA控制器寄存器,從而建立起設備 writeb(dev->registers.command, DAD_CMD_DISABLEDMA); //設置傳輸方向--讀還是寫 writeb(dev->registers.command, write ? DAD_CMD_WR : DAD_CMD_RD); writel(dev->registers.addr, cpu_to_le32(bus_addr));//buffer物理地址 writel(dev->registers.len, cpu_to_le32(count)); //傳輸的字節數 //開始激活DMA進行數據傳輸操作 writeb(dev->registers.command, DAD_CMD_ENABLEDMA); return 0; }
函數dad_interrupt是中斷處理函數,當DMA傳輸完時,調用這個中斷函數來取消buffer上的DMA映射,從而讓內核程序可以訪問這個buffer。
void dad_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct dad_dev *dev = (struct dad_dev *) dev_id;
/* Make sure it's really our device interrupting */
/* Unmap the DMA buffer */ pci_unmap_single(dev->pci_dev, dev->dma_addr, dev->dma_size, dev->dma_dir);
/* Only now is it safe to access the buffer, copy to user, etc. */ ...
}
函數dad_open打開設備,此時應申請中斷號及DMA通道。
int dad_open (struct inode *inode, struct file *filp)
{
struct dad_device *my_device;
// SA_INTERRUPT表示快速中斷處理且不支持共享 IRQ 信號線 if ( (error = request_irq(my_device.irq, dad_interrupt, SA_INTERRUPT, "dad", NULL)) ) return error; /* or implement blocking open */
if ( (error = request_dma(my_device.dma, "dad")) ) { free_irq(my_device.irq, NULL); return error; /* or implement blocking open */ }
return 0;
}
在與open 相對應的 close 函數中應該釋放DMA及中斷號。
void dad_close (struct inode *inode, struct file *filp)
{
struct dad_device *my_device; free_dma(my_device.dma); free_irq(my_device.irq, NULL); ……
}
函數dad_dma_prepare初始化DMA控制器,設置DMA控制器的寄存器的值,為 DMA 傳輸作准備。
int dad_dma_prepare(int channel, int mode, unsigned int buf,
unsigned int count)
{
unsigned long flags;
flags = claim_dma_lock(); disable_dma(channel); clear_dma_ff(channel); set_dma_mode(channel, mode); set_dma_addr(channel, virt_to_bus(buf)); set_dma_count(channel, count); enable_dma(channel); release_dma_lock(flags);
return 0;
}
函數dad_dma_isdone用來檢查 DMA 傳輸是否成功結束。
int dad_dma_isdone(int channel) { int residue; unsigned long flags = claim_dma_lock (); residue = get_dma_residue(channel); release_dma_lock(flags); return (residue == 0); }