《Linux Device Drivers》第十五章 内存映射和DMA——note
生活随笔
收集整理的這篇文章主要介紹了
《Linux Device Drivers》第十五章 内存映射和DMA——note
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
- 簡(jiǎn)單介紹
- 很多類型的驅(qū)動(dòng)程序編程都須要了解一些虛擬內(nèi)存子系統(tǒng)怎樣工作的知識(shí)
- 當(dāng)遇到更為復(fù)雜、性能要求更為苛刻的子系統(tǒng)時(shí),本章所討論的內(nèi)容遲早都要用到
- 本章的內(nèi)容分成三個(gè)部分
- 講述mmap系統(tǒng)調(diào)用的實(shí)現(xiàn)過程
- 講述怎樣跨越邊界直接訪問用戶空間的內(nèi)存頁(yè)
- 講述了直接內(nèi)存訪問(DMA)I/O操作,它使得外設(shè)具有直接訪問系統(tǒng)內(nèi)存的能力
- Linux的內(nèi)存管理
- 地址類型
- Linux是一個(gè)虛擬內(nèi)存系統(tǒng),這意味著用戶程序所使用的地址與硬件使用的物理地址是不等同的
- 有了虛擬內(nèi)存,在系統(tǒng)中執(zhí)行的程序能夠分配比物理內(nèi)存很多其它的內(nèi)存,甚至一個(gè)單獨(dú)的進(jìn)程都能擁有比系統(tǒng)物理內(nèi)存很多其它的虛擬地址空間
- 以下是一個(gè)Linux使用的地址類型列表
- 用戶虛擬地址
- 這是在用戶空間程序所能看到的常規(guī)地址
- 物理地址
- 該地址在處理器和系統(tǒng)內(nèi)存之間使用
- 總線地址
- 該地址在外圍總線和內(nèi)存之間使用,通常它們與處理器使用的物理地址同樣
- 內(nèi)核邏輯地址
- 內(nèi)核邏輯地址組成了內(nèi)核的常規(guī)地址空間
- 在大多數(shù)體系架構(gòu)中。邏輯地址和與其相關(guān)聯(lián)的物理地址不同,只在它們之間存在一個(gè)固定的偏移量
- kmalloc返回的內(nèi)存就是內(nèi)核邏輯地址
- 內(nèi)核虛擬地址
- 和內(nèi)核邏輯地址的同樣之處在于。它們都將內(nèi)核空間的地址映射到物理地址上
- 內(nèi)核虛擬地址與物理地址的映射不必是線性的一對(duì)一的
- 全部的邏輯地址都是內(nèi)核虛擬地址。可是非常多內(nèi)核虛擬地址不是邏輯地址
- vmalloc分配的內(nèi)存具有一個(gè)虛擬地址
- 用戶虛擬地址
- <asm/page.h>
- __pa()
- 返回其相應(yīng)的物理地址
- __va()
- 將物理地址逆向映射到邏輯地址,但這僅僅對(duì)低端內(nèi)存頁(yè)有效
- __pa()
- 物理地址和頁(yè)
- 物理地址被分成離散的單元。稱之為頁(yè)
- <asm/page.h>
- PAGE_SIZE
- 眼下大多數(shù)系統(tǒng)都使用每頁(yè)4096個(gè)字節(jié)
- 高端與低端內(nèi)存
- 使用32位系統(tǒng)僅僅能在4GB的內(nèi)存中尋址
- 內(nèi)核將4GB的虛擬地址空間切割為用戶空間和內(nèi)核空間,一個(gè)典型的切割是將3GB分配給用戶空間。1GB分配給內(nèi)核空間
- 低端內(nèi)存
- 存在于內(nèi)核空間上的邏輯地址內(nèi)存
- 高端內(nèi)存
- 那些不存在邏輯地址的內(nèi)存
- 內(nèi)存映射和頁(yè)結(jié)構(gòu)
- <linux/mm.h>
- struct page
- atomic_t count;
- 對(duì)該頁(yè)的訪問計(jì)數(shù)。
當(dāng)計(jì)數(shù)值為0時(shí),該頁(yè)將返回給空暇鏈表
- 對(duì)該頁(yè)的訪問計(jì)數(shù)。
- void *virtual;
- 假設(shè)頁(yè)面被映射。則指向頁(yè)的內(nèi)核虛擬地址;假設(shè)未被映射則為NULL
- unsigned long flags;
- 描寫敘述頁(yè)狀態(tài)的一系列標(biāo)志
- PG_locked表示內(nèi)存中的頁(yè)已經(jīng)被鎖住
- PG_reserved表示禁止內(nèi)存管理系統(tǒng)訪問該頁(yè)
- atomic_t count;
- struct page *virt_to_page(void *kaddr);
- struct page *pfn_to_page(int pfn);
- 針對(duì)給定的頁(yè)幀號(hào),返回page結(jié)構(gòu)指針
- void *page_address(struct page *page);
- 返回頁(yè)的內(nèi)核虛擬地址
- <linux/highmem.h>
- <asm/kmap_types.h>
- void *kmap(struct page *page);
- 對(duì)于低端內(nèi)存頁(yè)來(lái)說,返回頁(yè)的邏輯地址
- 對(duì)于高端內(nèi)存,在專用的內(nèi)核地址空間創(chuàng)建特殊的映射
- void kunmap(struct page *page);
- void *kmap_atomic(struct page *page, enum km_type type);
- void kunmap_atomic(void *addr, enum km_type type);
- 頁(yè)表
- 處理器必須使用某種機(jī)制同,將虛擬地址轉(zhuǎn)換為對(duì)應(yīng)的物理地址。這樣的機(jī)制被稱為頁(yè)表
- 它基本上是一個(gè)多層樹形結(jié)構(gòu)。結(jié)構(gòu)化的數(shù)據(jù)中包括了虛擬地址到物理地址的映射和相關(guān)的標(biāo)志位
- 虛擬內(nèi)存區(qū)
- 虛擬內(nèi)存區(qū)(VMA)用于管理進(jìn)程地址空間中不同區(qū)域的內(nèi)核數(shù)據(jù)結(jié)構(gòu)
- 進(jìn)程的內(nèi)存映射包括以下這些區(qū)域
- 程序的可運(yùn)行代碼區(qū)域
- 多個(gè)數(shù)據(jù)區(qū),當(dāng)中包括初始化數(shù)據(jù)、非初始化數(shù)據(jù)以及程序堆棧
- 與每一個(gè)活動(dòng)的內(nèi)存映射相應(yīng)的區(qū)域
- /proc/<pid>/maps
- start-end perm offset major:minor inode image
- vm_area_struct結(jié)構(gòu)
- <linux/mm.h>
- struct vm_area_struct
- unsigned long vm_start;
- unsigned long vm_end;
- struct file *vm_file;
- unsigned long vm_pgoff;
- unsigned long vm_flags;
- struct vm_operations_struct *vm_ops;
- void *vm_private_data;
- struct vm_operations_struct
- void (*open) (struct vm_area_struct *vma);
- void (*close) (struct vm_area_struct *vma);
- struct page *(*nopage) (struct vm_area_struct *vma, unsigned long address, int *type);
- int (*populate) (struct vm_area_struct *vm, unsigned long address, unsigned long len, pgprot_t prot, unsigned long pgoff, int nonblock);
- 內(nèi)存映射處理
- <linux/sched.h>
- struct mm_struct
- current->mm
- <linux/sched.h>
- 地址類型
- mmap設(shè)備操作
- 內(nèi)存映射能夠提供給用戶程序直接訪問設(shè)備內(nèi)存的能力
- 映射一個(gè)設(shè)備意味著將用戶空間的一段內(nèi)存與設(shè)備內(nèi)存關(guān)聯(lián)起來(lái)
- 像串口和其它面向流的設(shè)備就不能進(jìn)行mmap抽象
- 必須以PAGE_SIZE為單位進(jìn)行映射
- mmap方法是file_operations結(jié)構(gòu)的一部分
- mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t offset)
- int (*mmap) (struct file *filp, struct vm_area_struct *vma);
- 有兩種建立頁(yè)表的方法
- 使用remap_pfn_range函數(shù)一次所有建立
- 通過nopage VMA方法每次建立一個(gè)頁(yè)表
- 使用remap_pfn_range
- int rempa_pfn_range(struct vm_area_struct *vma, unsigned long virt_addr, unsigned long pfn, unsigned long size, pgprot_t prot);
- int io_remap_page_range(struct vm_area_struct *vma, unsigned long virt_addr, unsigned long phys_addr, unsigned long size, pgprot_t prot);
- vma
- 虛擬內(nèi)存區(qū)域
- virt_addr
- 又一次映射時(shí)的起始用戶虛擬地址
- pfn
- 與物理內(nèi)存相應(yīng)的頁(yè)幀號(hào)。虛擬內(nèi)存將要被映射到該物理內(nèi)存
- 頁(yè)幀號(hào)僅僅是將物理地址右移PAGE_SHIFT位
- size
- 以字節(jié)為單位
- prot
- 新VMA要求的“保護(hù)(protection)”屬性
- 一個(gè)簡(jiǎn)單的實(shí)現(xiàn)
- drivers/char/mem.c
- remap_pfn_range(vma, vma->vm_start, vm_.vm_pgoff, vma->vm_end – vma->vm_start, vma->vm_page_prot)
- 為VMA加入操作
- struct vm_operations_struct simple_remap_vm_ops = {.open = simple_vma_open, .close = simple_vma_close,}
- 使用nopage映射內(nèi)存
- 假設(shè)要支持mremap系統(tǒng)調(diào)用。就必須實(shí)現(xiàn)nopage函數(shù)
- struct page *(*nopage) (struct vm_area_struct *vma, unsigned long address, int *type);
- get_page(struct page *pageptr);
- static int simple_nopage_mmap(struct file *filp, struct vm_area_struct *vma)
- {
- unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
- if (offset >= __pa(high_memory) || (filp->f_flags & O_SYNC))
- vm->vm_flags |= VM_IO
- vm->vm_flags |= VM_RESERVED;
- vm->vm_ops = &simple_nopage_vm_ops;
- simple_vma_open(vma);
- return 0;
- }
- struct page *simple_vma_nopage(struct vm_area_struct *vma, unsigned long address, int *type)
- {
- struct page *pageptr;
- unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
- unsigned long physaddr = address – vma->vm_start + offset;
- unsigned long pageframe = physaddr >> PAGE_SHIFT;
- if (!pfn_valid(pageframe))
- return NOPAGE_SIGBUS;
- pageptr = pfn_to_page(pageframe);
- get_page(pageptr);
- if (type)
-
- type = VM_FAULT_MINOR;
-
- return pageptr;
- }
- 又一次映射RAM
- 對(duì)remap_pfn_range函數(shù)的一個(gè)限制是:它僅僅能訪問保留頁(yè)和超出物理內(nèi)存的物理地址
- remap_pfn_range不同意又一次映射常規(guī)地址
- 使用nopage方法又一次映射RAM
- 使用vm_ops->nopage一次處理一個(gè)頁(yè)錯(cuò)誤
- 又一次映射內(nèi)核虛擬地址
- page = vmalloc_to_page(pageptr);
- get_page(page);
- 運(yùn)行直接I/O訪問
- 假設(shè)須要傳輸?shù)臄?shù)據(jù)量很大。直接進(jìn)行傳輸數(shù)據(jù)。而不須要額外地從內(nèi)核空間拷貝數(shù)據(jù)操作的參與,這將會(huì)大大提快速度
- 設(shè)置直接I/O的開銷很巨大
- 使用直接I/O須要write系統(tǒng)調(diào)用同步運(yùn)行
- 在每一個(gè)寫操作完畢之前不能停止應(yīng)用程序
- <linux/mm.h>
- int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, unsigned long start, int len, int write, int force, struct page **pages, struct vm-area_struct **vmas);
- tsk
- 指向運(yùn)行I/O的任務(wù)指針,該參數(shù)差點(diǎn)兒是current
- mm
- 指向描寫敘述被映射地址空間的內(nèi)存管理結(jié)構(gòu)的指針
- 對(duì)驅(qū)動(dòng)程序來(lái)說。該參數(shù)總是current->mm
- force
- 假設(shè)write非零。對(duì)映射的頁(yè)有寫權(quán)限
- 驅(qū)動(dòng)程序?qū)υ搮?shù)總是設(shè)置為0
- pages
- 假設(shè)調(diào)用成功,pages中包括了一個(gè)描寫敘述用戶空間緩沖區(qū)page結(jié)構(gòu)的指針列表
- vmas
- 假設(shè)調(diào)用成功,vmas包括了對(duì)應(yīng)VMA的指針
- tsk
- int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, unsigned long start, int len, int write, int force, struct page **pages, struct vm-area_struct **vmas);
- 使用直接I/O的設(shè)備通常使用DMA操作
- 一旦直接I/O操作完畢,就必須釋放用戶內(nèi)存頁(yè)
- <linux/page-flags.h>
- void SetPageDirty(struct page *page);
- void page_cache_release(struct page *page);
- 異步I/O
- <linux/aio.h>
- ssize_t (*aio_read) (struct kiocb *iocb, char *buffer, size_t count, loff_t offset);
- ssize_t (*aio_write) (struct kiocb *iocb, const char *buffer, size_t count, loff_t offset);
- int (*aio_fsync) (struct kiocb *iocb, int datasync);
- int is_sync_kiocb(struct kiocb *iocb);
- int aio_complete(struct kiocb *iocb, long res, long res2);
- 直接內(nèi)存訪問
- DMA是一種硬件機(jī)制同,它同意外圍設(shè)備和主內(nèi)存之間直接傳輸它們的I/O數(shù)據(jù)。而不須要系統(tǒng)處理器的參與
- 使用這樣的機(jī)制能夠大大提高與設(shè)備通信的吞吐量
- DMA傳輸數(shù)據(jù)概覽
- 有兩種方式引發(fā)傳輸數(shù)據(jù)
- 軟件對(duì)數(shù)據(jù)的請(qǐng)求
- 當(dāng)進(jìn)程調(diào)用read,驅(qū)動(dòng)程序函數(shù)分配一個(gè)DMA緩沖區(qū),并讓硬件將傳輸數(shù)據(jù)到這個(gè)緩沖區(qū)中,進(jìn)程處于睡眠狀態(tài)
- 硬件將數(shù)據(jù)寫入到DMA緩沖區(qū)中,當(dāng)寫入完成。產(chǎn)生一個(gè)中斷
- 中斷處理程序獲得輸入的數(shù)據(jù),應(yīng)答中斷。而且喚醒進(jìn)程,該進(jìn)程如今就可以讀取數(shù)據(jù)
- 硬件異步地將數(shù)據(jù)傳遞給系統(tǒng)
- 硬件產(chǎn)生中斷,宣告新數(shù)據(jù)的到來(lái)
- 中斷處理程序分配一個(gè)緩沖區(qū),而且告訴硬件向哪里數(shù)據(jù)傳輸
- 外圍設(shè)備將數(shù)據(jù)寫入緩沖區(qū),完畢后產(chǎn)生另外一個(gè)中斷
- 處理程序分發(fā)新數(shù)據(jù)。喚醒不論什么相關(guān)進(jìn)程,然后運(yùn)行清理工作
- 軟件對(duì)數(shù)據(jù)的請(qǐng)求
- 有兩種方式引發(fā)傳輸數(shù)據(jù)
- 分配DMA緩沖區(qū)
- 使用DMA緩沖區(qū)的主要問題是:當(dāng)大于一頁(yè)時(shí)。它們必須占領(lǐng)連接的物理頁(yè),這是由于設(shè)備使用ISA或者PCI系統(tǒng)總線數(shù)據(jù)傳輸,而這兩種方式使用的都是物理地址
- DIY分配
- get_free_pages函數(shù)能夠分配多達(dá)幾M字節(jié)的內(nèi)存,可是對(duì)較大數(shù)量的請(qǐng)求。甚至是遠(yuǎn)少于128KB的請(qǐng)求也一般會(huì)失敗,這是由于此時(shí)系統(tǒng)內(nèi)存中充滿了內(nèi)存碎片
- 當(dāng)內(nèi)核不能返回請(qǐng)求數(shù)量的內(nèi)存或須要超過128KB內(nèi)存時(shí),除了返回-ENOMEM,另外一個(gè)方法是在引導(dǎo)時(shí)分配內(nèi)存或是為緩沖區(qū)保留頂部物理RAM
- 另一個(gè)方法是使用GFP_NOFAIL分配標(biāo)志來(lái)為緩沖區(qū)分配內(nèi)存
- 總線地址
- 使用DMA的設(shè)備驅(qū)動(dòng)程序?qū)⑴c連接到總線接口上的硬件通信,硬件使用的是物理地址,而程序代碼使用的是虛擬地址
- <asm/io.h>
- unsigned long virt_to_bus(volatile void *address);
- void *bus_to_virt(unsigned long address);
- 通用DMA層
- 內(nèi)核提供了一個(gè)與總線體系架構(gòu)無(wú)關(guān)的DMA層
- <linux/dma-mapping.h>
- 處理復(fù)雜的硬件
- int dma_set_mask(struct device *dev, u64 mask);
- 該掩碼顯示與設(shè)備能尋址能力相應(yīng)的位
- 假設(shè)dma_set_mask返回0,則對(duì)該設(shè)備不能使用DMA
- int dma_set_mask(struct device *dev, u64 mask);
- DMA映射
- 一個(gè)DMA映射是要分配的DMA緩沖區(qū)與為該緩沖區(qū)生成的、設(shè)備可訪問地址的組合
- DMA映射建立了一個(gè)新的結(jié)構(gòu)類型——dma_addr_t來(lái)表示總線地址
- 依據(jù)DMA緩沖區(qū)期望保留的時(shí)間長(zhǎng)短。PCI代碼區(qū)分兩種類型的DMA映射
- 一致性DMA映射
- 這樣的類型的映射存在于驅(qū)動(dòng)程序生命周期中
- 一致性映射的緩沖區(qū)必須可同一時(shí)候被CPU和外圍設(shè)備訪問
- 建立和使用一致性映射的開銷是非常大的
- 流式DMA映射
- 通常為單獨(dú)的操作建立流式映射
- 內(nèi)核開發(fā)人員建議盡量使用流式映射,然后再考慮一致性映射
- 在支持映射寄存器的系統(tǒng)中,每一個(gè)DMA映射使用總線上的一個(gè)或者多個(gè)映射寄存器
- 在一些硬件中,流式映射能夠被優(yōu)化。但優(yōu)化的方法對(duì)一致性映射無(wú)效
- 一致性DMA映射
- 建立一致性DMA映射
- void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, int flag);
- 返回值是緩沖區(qū)的內(nèi)核虛擬地址
- 與其相關(guān)的總線地址,保存在dma_handle中
- void dma_free_coherent(struct device *dev, size_t size, void *vaddr, dma_addr_t dma_handle);
- void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, int flag);
- DMA池
- DMA池是一個(gè)生成小型、一致性DMA映射的機(jī)制
- <linux/dmapool.h>
- struct dma_pool *dma_pool_create(const char *name, struct device *dev, size_t size, size_t align, size_t allocation);
- allocation不為零,表示內(nèi)存邊界不能超越allocation
- void dma_pool_destroy(struct dma_pool *pool);
- void *dma_pool_alloc(struct dma_pool *pool, int mem_flags, dma_addr_t *handle);
- 返回的DMA緩沖區(qū)的地址是內(nèi)核虛擬地址
- void dma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t addr);
- struct dma_pool *dma_pool_create(const char *name, struct device *dev, size_t size, size_t align, size_t allocation);
- 建立流式DMA映射
- 當(dāng)建立流式映射時(shí)。必須告訴內(nèi)核數(shù)據(jù)流動(dòng)的方向
- enum dma_data_direction
- DMA_TO_DEVICE
- DMA_FROM_DEVICE
- DMA_BIDIRECTIONAL
- DMA_NONE
- dma_addr_t dma_map_single(struct device *dev, void *buffer, size_t size, enum dma_data_direction direction);
- void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, enum dma_data_direction direction);
- 有幾條很重要的原則用于流式DMA映射
- 緩沖區(qū)僅僅能用于這種傳送。即其傳送方向匹配于映射時(shí)給定的方向wfhg
- 一旦緩沖區(qū)被映射,它將屬于設(shè)備,而不是處理器
- 在DMA處于活動(dòng)期間內(nèi),不能撤銷對(duì)緩沖區(qū)映射,否則會(huì)嚴(yán)重破壞系統(tǒng)的穩(wěn)定性
- void dma_sync_single_for_cpu(struct device *dev, dma_handle_t bus_addr, size_t size, enum dma_data_direction direction);
- void dma_sync_single_for_device(struct device *dev, dma_handle_t bus_addr, size_t size, enum dma_data_direction direction);
- 單頁(yè)流式映射
- dma_addr_t dma_map_page(struct device *dev, struct page *page, unsigned long offset, size_t size, enum dma_data_direction direction);
- void dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size, enum dma_data_direction direction);
- 分散/聚焦映射
- 這是一種特殊的流式DMA映射
- 如果有幾個(gè)緩沖區(qū),它們須要與設(shè)備雙向數(shù)據(jù)傳輸
- 有幾種方式能產(chǎn)生這樣的情形
- 從raedv或者writev系統(tǒng)調(diào)用產(chǎn)生
- 從集群的磁盤I/O請(qǐng)求產(chǎn)生
- 從映射的內(nèi)核I/O緩沖區(qū)中的頁(yè)面鏈表產(chǎn)生
- 很多設(shè)備都能接受一個(gè)指針數(shù)組的分散表,以及它的長(zhǎng)度,然后在一次DMA操作中把它們所有傳輸走
- 映射分散表的第一步是建立并填充一個(gè)描寫敘述被傳送緩沖區(qū)的scatterlist結(jié)構(gòu)的數(shù)組
- <linux/scatterlist.h>
- struct scatterlist
- struct page *page;
- unsigned int length;
- unsigned int offset;
- int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction direction);
- nents是傳入的分散表入口的數(shù)量
- 返回值是要傳送的DMA緩沖區(qū)數(shù)
- 驅(qū)動(dòng)程序應(yīng)該傳輸由dma_map_sg函數(shù)返回的每一個(gè)緩沖區(qū)
- dma_addr_t sg_dma_address(struct scatterlist *sg);
- unsinged int sg_dma_len(struct scatterlist *sg);
- void dma_unmap_sg(struct device *dev, struct scatterlist *list, int nents, enum dma_data_direction direction);
- nents一定是先前傳遞給dma_map_sg函數(shù)的入口項(xiàng)的數(shù)量
- void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction direction);
- void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction direction);
- PCI雙重地址周期映射
- 通常DMA支持層使用32位總線地址。其為設(shè)備的DMA掩碼所約束
- PCI總線還支持64位地址模式。既雙重地址周期(DAC)
- 假設(shè)設(shè)備須要使用放在高端內(nèi)存的大塊緩沖區(qū),能夠考慮實(shí)現(xiàn)DAC支持
- <linux/pci.h>
- int pci_dac_set_dma_mask(struct pci_dev *pdev, u64 mask);
- 返回0時(shí)。才干使用DAC地址
- dma64_addr_t pci_dac_page_to_dma(struct pci_dev *pdev, struct page *page, unsigned long offset, int direction);
- direction
- PCI_DMA_TODEVICE
- PCI_DMA_FROMDEVICE
- PCI_DMA_BIDIRECTIONAL
- direction
- void pci_dac_dma_sync_single_for_cpu(struct pci_dev *pdev, dma64_addr_t dma_addr, size_t len, int direction);
- void pci_dac_dma_sync_single_for_device(struct pci_dev *pdev, dma64_addr_t dma_addr, size_t len, int direction);
- ISA設(shè)備的DMA
- ISA總線同意兩種DMA傳輸:本地(native)DMA和ISA總線控制(bus-master)DMA
- 本地DMA使用主板上的標(biāo)準(zhǔn)DMA控制器電路來(lái)驅(qū)動(dòng)ISA總線上的信號(hào)線
- ISA總線控制DMA全然由外圍設(shè)備控制
- 有三種實(shí)現(xiàn)涉及到ISA總線上的DMA傳輸數(shù)據(jù)
- 8237 DMA控制器(DMAC)
- 外圍設(shè)備
- 當(dāng)設(shè)備準(zhǔn)備傳送數(shù)據(jù)時(shí),必須激活DMA請(qǐng)求信號(hào)
- 設(shè)備驅(qū)動(dòng)程序
- 須要驅(qū)動(dòng)程序完畢的工作非常少,它僅僅是負(fù)責(zé)提供DMA控制器的方向、總線地址、傳輸量的大小等等
- 注冊(cè)DMA
- <asm/dma.h>
- int request_dma(unsigned int channel, const char *name);
- 返回0表示運(yùn)行成功
- void free_dma(unsigned int channel);
- int request_dma(unsigned int channel, const char *name);
- <asm/dma.h>
- 與DMA控制器通信
- unsigned long claim_dma_lock();
- 必須被裝入控制器的信息包括三個(gè)部分:RAM的地址、必須被傳輸?shù)脑禹?xiàng)個(gè)數(shù)以及傳輸?shù)姆较?/li>
- void set_dma_mode(unsigned int channel, char mode);
- mode
- DMA_MODE_READ
- DMA_MODE_WRITE
- DMA_MODE_CASCADE
- 釋放對(duì)總線的控制
- mode
- void set_dma_addr(unsigned int channel, unsigned int addr);
- void set_dma_count(unsigned int channel, unsigned int count);
- void disable_dma(unsigned int channel);
- void enable_dma(unsigned int channel);
- int get_dma_residue(unsigned int channel);
- 返回還未傳輸?shù)淖止?jié)數(shù)
- void clear_dma_ff(unsigned int channel);
總結(jié)
以上是生活随笔為你收集整理的《Linux Device Drivers》第十五章 内存映射和DMA——note的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 关于App开发:模拟服务器数据接口 -
- 下一篇: Java中String、StringBu