欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
15.4.?直接內存存取-Linux設備驅動(dòng)第三版(中文版)

15.4. 直接內存存取

直接內存存取, 或者 DMA, 是結束我們的內存問(wèn)題概覽的高級主題. DMA 是硬件機制允許外設組件來(lái)直接傳輸它們的 I/O 數據到和從主內存, 而不需要包含系統處理器. 這種機制的使用能夠很大提高吞吐量到和從一個(gè)設備, 因為大量的計算開(kāi)銷(xiāo)被削減了.

在介紹程序細節之前, 讓我們回顧一個(gè) DMA 傳輸如何發(fā)生的, 只考慮輸入傳輸來(lái)簡(jiǎn)化討論.

數據傳輸可由 2 種方法觸發(fā):或者軟件請求數據(通過(guò)一個(gè)函數例如 read)或者硬件異步推數據到系統.

在第一種情況, 包含的步驟總結如下:

  • 1. 當一個(gè)進(jìn)程調用 read, 驅動(dòng)方法分配一個(gè) DMA 緩沖并引導硬件來(lái)傳輸它的數據到那個(gè)緩沖. 這個(gè)進(jìn)程被置為睡眠.

  • 2. 硬件寫(xiě)數據到這個(gè) DMA 緩沖并且在它完成時(shí)引發(fā)一個(gè)中斷.

  • 3. 中斷處理獲得輸入數據, 確認中斷, 并且喚醒進(jìn)程, 它現在可以讀數據了.

第 2 種情況到來(lái)是當 DMA 被異步使用. 例如, 這發(fā)生在數據獲取設備, 它在沒(méi)有人讀它們的時(shí)候也持續推入數據. 在這個(gè)情況下, 驅動(dòng)應當維護一個(gè)緩沖以至于后續的讀調用能返回所有的累積的數據給用戶(hù)空間. 這類(lèi)傳輸包含的步驟有點(diǎn)不同:

  • 1. 硬件引發(fā)一個(gè)中斷來(lái)宣告新數據已經(jīng)到達.

  • 2. 中斷處理分配一個(gè)緩沖并且告知硬件在哪里傳輸數據.

  • 3. 外設寫(xiě)數據到緩沖并且引發(fā)另一個(gè)中斷當完成時(shí).

  • 處理者分派新數據, 喚醒任何相關(guān)的進(jìn)程, 并且負責雜務(wù).

異步方法的變體常常在網(wǎng)卡中見(jiàn)到. 這些卡常常期望見(jiàn)到一個(gè)在內存中和處理器共享的環(huán)形緩沖(常常被稱(chēng)為一個(gè) DMA 的緩沖);每個(gè)到來(lái)的報文被放置在環(huán)中下一個(gè)可用的緩沖, 并且發(fā)出一個(gè)中斷. 驅動(dòng)接著(zhù)傳遞網(wǎng)絡(luò )本文到內核其他部分并且在環(huán)中放置一個(gè)新 DMA 緩沖.

在所有這些情況中的處理的步驟都強調, 有效的 DMA 處理依賴(lài)中斷報告. 雖然可能實(shí)現 DMA 使用一個(gè)輪詢(xún)驅動(dòng), 它不可能有意義, 因為一個(gè)輪詢(xún)驅動(dòng)可能浪費 DMA 提供的性能益處超過(guò)更容易的處理器驅動(dòng)的I/O.[49]

在這里介紹的另一個(gè)相關(guān)項是 DMA 緩沖. DMA 要求設備驅動(dòng)來(lái)分配一個(gè)或多個(gè)特殊的適合 DMA 的緩沖. 注意許多驅動(dòng)分配它們的緩沖在初始化時(shí)并且使用它們直到關(guān)閉 -- 在之前列表中的分配一詞, 意思是"獲得一個(gè)之前分配的緩沖".

本節涵蓋 DMA 緩沖在底層的分配; 我們稍后介紹一個(gè)高級接口, 但是來(lái)理解這里展示的內容仍是一個(gè)好主意.

隨 DMA 緩沖帶來(lái)的主要問(wèn)題是, 當它們大于一頁(yè), 它們必須占據物理內存的連續頁(yè)因為設備使用 ISA 或者 PCI 系統總線(xiàn)傳輸數據,它們都使用物理地址. 注意有趣的是這個(gè)限制不適用 SBus ( 見(jiàn) 12 章的"SBus"一節 ), 它在外設總線(xiàn)上使用虛擬地址.一些體系結構還可以在 PCI 總線(xiàn)上使用虛擬地址, 但是一個(gè)可移植的驅動(dòng)不能依賴(lài)這個(gè)功能.

盡管 DMA 緩沖可被分配或者在系統啟動(dòng)時(shí)或者在運行時(shí), 模塊只可在運行時(shí)分配它們的緩沖. (第 8 章介紹這些技術(shù);"獲取大緩沖"一節涵蓋在系統啟動(dòng)時(shí)分配, 而"kmalloc 的真實(shí)"和"get_free_page 和其友"描述在運行時(shí)分配).驅動(dòng)編寫(xiě)者必須關(guān)心分配正確的內存,當它被用做 DMA 操作時(shí); 不是所有內存區是合適的. 特別的, 在一些系統中的一些設備上高端內存可能不為DMA 工作 - 外設完全無(wú)法使用高端地址.

在現代總線(xiàn)上的大部分設備可以處理 32-位 地址, 意思是正常的內存分配對它們是剛剛好的. 一些 PCI 設備, 但是, 不能實(shí)現完整的 PCI 標準并且不能使用 32-位 地址. 并且 ISA 設備, 當然, 限制只在 24-位 地址.

對于有這種限制的設備, 內存應當從 DMA 區進(jìn)行分配, 通過(guò)添加 GFP_DMA 標志到 kmalloc 或者get_free_pages 調用. 當這個(gè)標志存在, 只有可用 24-位 尋址的內存被分配. 另一種選擇, 你可以使用通用的 DMA 層(我們馬上討論這個(gè) )來(lái)分配緩沖以解決你的設備的限制.

15.4.2.1. 自己做分配

我們已見(jiàn)到 get_free_pages 如何分配直到幾個(gè) MByte (由于 order 可以直到 MAX_ORDER, 當前是 11), 但是高級數的請求容易失敗當請求的緩沖遠遠小于 128 KB, 因為系統內存時(shí)間長(cháng)了變得碎裂.[50]

當內核無(wú)法返回請求數量的內存或者當你需要多于 128 KB(例如, 一個(gè)通常的 PCI 幀抓取的請求), 一個(gè)替代返回 -ENOMEM的做法是在啟動(dòng)時(shí)分配內存或者保留物理 RAM 的頂部給你的緩沖. 我們在第 8 章的 "獲得大量緩沖" 一節描述在啟動(dòng)時(shí)間分配,但是它對模塊是不可用的. 保留 RAM 的頂部是通過(guò)在啟動(dòng)時(shí)傳遞一個(gè) mem= 參數給內核實(shí)現的. 例如, 如果你有 256 MB, 參數mem=255M 使內核不使用頂部的 MByte. 你的模塊可能后來(lái)使用下列代碼來(lái)獲得對這個(gè)內存的存取:

dmabuf = ioremap (0xFF00000 /* 255M */, 0x100000 /* 1M */);

分配器, 配合本書(shū)的例子代碼的一部分, 提供了一個(gè)簡(jiǎn)單的 API 來(lái)探測和管理這樣的保留 RAM 并且已在幾個(gè)體系上被成功使用. 但是, 這個(gè)技巧當你有一個(gè)高內存系統時(shí)無(wú)效(即, 一個(gè)有比適合 CPU 地址空間更多的物理內存的系統 ).

當然, 另一個(gè)選項, 是使用 GFP_NOFAIL 來(lái)分配你的緩沖. 這個(gè)方法, 但是, 確實(shí)嚴重地對內存管理子系統有壓力, 并且它冒鎖住系統的風(fēng)險; 最好是避免除非確實(shí)沒(méi)有其他方法.

如果你分配一個(gè)大 DMA 緩沖到這樣的長(cháng)度, 但是, 值得想一下替代的方法. 如果你的設備可以做發(fā)散/匯聚 I/O,你可以分配你的緩沖以更小的片段并且讓設備做其他的. 發(fā)散/匯聚 I/O 也可以用當進(jìn)行直接 I/O 到用戶(hù)空間時(shí),它可能是最好地解決方法當需要一個(gè)真正大緩沖時(shí).

15.4.3. 總線(xiàn)地址

一個(gè)使用 DMA 的設備驅動(dòng)必須和連接到接口總線(xiàn)的硬件通訊, 總線(xiàn)使用物理地址, 而程序代碼使用虛擬地址.

事實(shí)上, 情況比這個(gè)稍微有些復雜. 基于DMA 的硬件使用總線(xiàn)地址, 而不是物理地址. 盡管 ISA 和 PCI 總線(xiàn)地址在 PC上完全是物理地址, 這對每個(gè)平臺卻不總是真的. 有時(shí)接口總線(xiàn)被通過(guò)橋接電路連接, 它映射 I/O 地址到不同的物理地址.一些系統甚至有一個(gè)頁(yè)映射機制, 使任意的頁(yè)連續出現在外設總線(xiàn).

在最低級別(再次, 我們將馬上查看一個(gè)高級解決方法), Linux 內核提供一個(gè)可移植的方法, 通過(guò)輸出下列函數, 在<asm/io.h> 定義. 這些函數的使用不被推薦, 因為它們只在有非常簡(jiǎn)單的 I/O 體系的系統上正常工作; 但是,你可能遇到它們當使用內核代碼時(shí).

unsigned long virt_to_bus(volatile void *address);void *bus_to_virt(unsigned long address);

這些函數進(jìn)行一個(gè)簡(jiǎn)單的轉換在內核邏輯地址和總線(xiàn)地址之間. 它們在許多情況下不工作, 一個(gè) I/O 內存管理單元必須被編程的地方或者必須使用反彈緩沖的地方. 做這個(gè)轉換的正確方法是使用通用的 DMA 層, 因此我們現在轉移到這個(gè)主題.

15.4.4. 通用 DMA 層

DMA 操作, 最后, 下到分配一個(gè)緩沖并且傳遞總線(xiàn)地址到你的設備. 但是, 編寫(xiě)在所有體系上安全并正確進(jìn)行 DMA的可移植啟動(dòng)的任務(wù)比想象的要難. 不同的系統有不同的概念, 關(guān)于緩存一致性應當如何工作的概念; 如果你不正確處理這個(gè)問(wèn)題,你的驅動(dòng)可能破壞內存. 一些系統有復雜的總線(xiàn)硬件, 它使 DMA 任務(wù)更容易 - 或者更難. 并且不是所有的系統可以在內存所有部分進(jìn)行 DMA.幸運的是, 內核提供了一個(gè)總線(xiàn)和體系獨立的 DMA 層來(lái)對驅動(dòng)作者隱藏大部分這些問(wèn)題. 我們非常鼓勵你來(lái)使用這個(gè)層來(lái) DMA 操作,在任何你編寫(xiě)的驅動(dòng)中.

下面的許多函數需要一個(gè)指向 struct device 的指針. 這個(gè)結構是 Linux 設備模型中設備的低級表示.它不是驅動(dòng)常常必須直接使用的東西, 但是你確實(shí)需要它當使用通用 DMA 層時(shí). 常常地, 你可發(fā)現這個(gè)結構, 深埋在描述你的設備的總線(xiàn). 例如,它可在 struct pci_device 或者 struct usb_device 中發(fā)現它作為 dev 成員. 設備結構在 14章中詳細描述.

使用下面函數的驅動(dòng)應當包含 <linux/dma-mapping.h>.

15.4.4.1. 處理困難硬件

在嘗試 DMA 之前必須回答的第一個(gè)問(wèn)題是給定設備是否能夠在當前主機上做這樣的操作. 許多設備受限于它們能夠尋址的內存范圍,因為許多理由. 缺省地, 內核假定你的設備能夠對任何 32-位 地址進(jìn)行 DMA. 如果不是這樣, 你應當通知內核這個(gè)事實(shí), 使用一個(gè)調用:

 int dma_set_mask(struct device *dev, u64 mask);

mask 應當顯示你的設備能夠尋址的位; 如果它被限制到 24 位, 例如, 你要傳遞 mask 作為 0x0FFFFFF.返回值是非零如果使用給定的 mask 可以 DMA; 如果 dma_set_mask 返回 0, 你不能對這個(gè)設備使用 DMA 操作. 因此,設備的驅動(dòng)中的初始化代碼限制到 24-位 DMA 操作可能看來(lái)如:

if (dma_set_mask (dev, 0xffffff))card->use_dma = 1;else{card->use_dma = 0; /* We'll have to live without DMA */printk (KERN_WARN, "mydev: DMA not supported\n");}

再次, 如果你的設備支持正常的, 32-位 DMA 操作, 沒(méi)有必要調用 dma_set_mask.

15.4.4.2. DMA 映射

一個(gè) DMA 映射是分配一個(gè) DMA 緩沖和產(chǎn)生一個(gè)設備可以存取的地址的結合. 它試圖使用一個(gè)簡(jiǎn)單的對 virt_to_bus的調用來(lái)獲得這個(gè)地址, 但是有充分的理由來(lái)避免那個(gè)方法. 它們中的第一個(gè)是合理的硬件帶有一個(gè) IOMMU 來(lái)為總線(xiàn)提供一套映射寄存器.IOMMU 可為任何物理內存安排來(lái)出現在設備可存取的地址范圍內, 并且它可使物理上散布的緩沖對設備看來(lái)是連續的. 使用 IOMMU需要使用通用的 DMA 層; virt_to_bus 不負責這個(gè)任務(wù).

注意不是所有的體系都有一個(gè) IOMMU; 特別的, 流行的 x86 平臺沒(méi)有 IOMMU 支持. 一個(gè)正確編寫(xiě)的驅動(dòng)不需要知道它在之上運行的 I/O 支持硬件, 但是.

為設備設置一個(gè)有用的地址可能也, 在某些情況下, 要求一個(gè)反彈緩沖的建立. 反彈緩沖是當一個(gè)驅動(dòng)試圖在一個(gè)外設不能達到的地址上進(jìn)行 DMA時(shí)創(chuàng )建的, 比如一個(gè)高內存地址. 數據接著(zhù)根據需要被拷貝到和從反彈緩沖. 無(wú)需說(shuō), 反彈緩沖的使用能拖慢事情, 但是有時(shí)沒(méi)有其他選擇.

DMA 映射也必須解決緩存一致性問(wèn)題. 記住現代處理器保持最近存取的內存區的拷貝在一個(gè)快速的本地緩沖中; 如果沒(méi)有這個(gè)緩存,合理的性能是不可能的. 如果你的設備改變主存一個(gè)區, 會(huì )強制使任何包含那個(gè)區的處理器緩存被失效; 負責處理器可能使用不正確的主存映象,并且導致數據破壞. 類(lèi)似地, 當你的設備使用 DMA 來(lái)從主存中讀取數據, 任何對那個(gè)駐留在處理器緩存的內存的改變必須首先被刷新.這些緩存一致性問(wèn)題可以產(chǎn)生無(wú)頭的模糊和難尋的錯誤, 如果編程者不小心. 一個(gè)體系在硬件中管理緩存一致性, 但是其他的要求軟件支持. 通用的DMA 層深入很多來(lái)保證在所有體系上事情都正確工作, 但是, 如同我們將見(jiàn)到的, 正確的行為要求符合一些規則.

DMA 映射設置一個(gè)新類(lèi)型, dma_addr_t, 來(lái)代表總線(xiàn)地址. 類(lèi)型 dma_addr_t 的變量應當被驅動(dòng)當作不透明的;唯一可允許的操作是傳遞它們到 DMA 支持過(guò)程和設備自身. 作為一個(gè)總線(xiàn)地址, dma_addr_t 可導致不期望的問(wèn)題如果被 CPU直接使用.

PCI 代碼在 2 類(lèi) DMA 映射中明顯不同, 依賴(lài) DMA 緩沖被期望停留多長(cháng)時(shí)間:

Coherent DMA mappings

連貫的 DMA 映射. 這些映射常常在驅動(dòng)的生命期內存在. 一個(gè)連貫的緩沖必須是同時(shí)對 CPU 和外設可用(其他的映射類(lèi)型,如同我們之后將看到的, 在任何給定時(shí)間只對一個(gè)或另一個(gè)可用). 結果, 一致的映射必須在緩沖一致的內存. 一致的映射建立和使用可能是昂貴的.

Streaming DMA mappings

流 DMA 映射. 流映射常常為一個(gè)單個(gè)操作建立. 一些體系當使用流映射時(shí)允許大的優(yōu)化, 如我們所見(jiàn),但是這些映射也服從一個(gè)更嚴格的關(guān)于如何存取它們的規則. 內核開(kāi)發(fā)者建議使用一致映射而不是流映射在任何可能的時(shí)候. 這個(gè)建議有 2 個(gè)原因.第一個(gè), 在支持映射寄存器的系統上, 每個(gè) DMA 映射在總線(xiàn)上使用它們一個(gè)或多個(gè). 一致映射, 有長(cháng)的生命周期, 可以長(cháng)時(shí)間獨占這些寄存器,甚至當它們不在使用時(shí). 另外一個(gè)原因是, 在某些硬件上, 流映射可以用無(wú)法在一致映射中使用的方法來(lái)優(yōu)化.

這 2 種映射類(lèi)型必須以不同的方式操作; 是時(shí)候看看細節了.

15.4.4.3. 建立一致 DMA 映射

一個(gè)驅動(dòng)可以建立一個(gè)一致映射, 使用對 dma_alloc_coherent 的調用:

void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, int flag);

這個(gè)函數處理緩沖的分配和映射. 前 2 個(gè)參數是設備結果和需要的緩沖大小. 這個(gè)函數返回 DMA 映射的結果在 2 個(gè)地方.來(lái)自這個(gè)函數的返回值是緩沖的一個(gè)內核虛擬地址, 它可被驅動(dòng)使用; 其間相關(guān)的總線(xiàn)地址在 dma_handle 中返回.分配在這個(gè)函數中被處理以至緩沖被放置在一個(gè)可以使用 DMA 的位置; 常常地內存只是使用 get_free_pages來(lái)分配(但是注意大小是以字節計的, 而不是一個(gè) order 值). flag 參數是通常的 GFP_ 值來(lái)描述內存如何被分配; 常常應當是GFP_KERNEL (常常) 或者 GFP_ATOMIC (當在原子上下文中運行時(shí)).

當不再需要緩沖(常常在模塊卸載時(shí)), 它應當被返回給系統, 使用 dma_free_coherent:

void dma_free_coherent(struct device *dev, size_t size,void *vaddr, dma_addr_t dma_handle);

注意, 這個(gè)函數象許多通常的 DMA 函數, 需要提供所有的大小, CPU 地址, 和 總線(xiàn)地址參數.

15.4.4.4. DMA 池

一個(gè) DMA池 是分配小的, 一致DMA映射的分配機制. 從 dma_alloc_coherent 獲得的映射可能有一頁(yè)的最小大小.如果你的驅動(dòng)需要比那個(gè)更小的 DMA 區域, 你應當可能使用一個(gè) DMA 池. DMA 池也在這種情況下有用,當你可能試圖對嵌在一個(gè)大結構中的小區域進(jìn)行 DMA 操作. 一些非常模糊的驅動(dòng)錯誤已被追蹤到緩存一致性問(wèn)題, 在靠近小 DMA 區域的結構成員.為避免這個(gè)問(wèn)題, 你應當一直明確分配進(jìn)行 DMA 操作的區域, 和其他的非 DMA 數據結構分開(kāi).

DMA 池函數定義在 <linux/dmapool.h>.

一個(gè) DMA 池必須在使用前創(chuàng )建, 使用一個(gè)調用:

struct dma_pool *dma_pool_create(const char *name, struct device *dev,size_t size, size_t align,size_t allocation);

這里, name 是池的名子, dev 是你的設備結構, size 是要從這個(gè)池分配的緩沖區大小, align是來(lái)自池的分配要求的硬件對齊(以字節表達的), 以及 allocation是, 如果非零, 一個(gè)分配不應當越過(guò)的內存邊界. 如果allocation 以 4096 傳遞, 例如, 從池分配的緩沖不越過(guò) 4-KB 邊界.

當你用完一個(gè)池, 可被釋放, 用:

void dma_pool_destroy(struct dma_pool *pool);

你應當返回所有的分配給池, 在銷(xiāo)毀它之前. 分配被用 dma_pool_alloc 處理:

void *dma_pool_alloc(struct dma_pool *pool, int mem_flags, dma_addr_t *handle);

對這個(gè)調用, mem_flags 是常用的 GFP_ 分配標志的設置. 如果所有都進(jìn)行順利,一個(gè)內存區(大小是當池創(chuàng )建時(shí)指定的)被分配和返回. 至于 dam_alloc_coherent, 結果 DMA緩沖地址被返回作為一個(gè)內核虛擬地址, 并作為一個(gè)總線(xiàn)地址被存于 handle.

不需要的緩沖應當返回池, 使用:

void dma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t addr);

15.4.4.5. 建立流 DMA 映射

流映射比一致映射有更復雜的接口, 有幾個(gè)原因. 這些映射行為使用一個(gè)由驅動(dòng)已經(jīng)分配的緩沖, 因此, 必須處理它們沒(méi)有選擇的地址. 在一些體系上, 流映射也可以有多個(gè)不連續的頁(yè)和多部分的"發(fā)散/匯聚"緩沖. 所有這些原因, 流映射有它們自己的一套映射函數.

當建立一個(gè)流映射時(shí), 你必須告知內核數據移向哪個(gè)方向. 一些符號(enum dam_data_direction 類(lèi)型)已為此定義:

DMA_TO_DEVICE
DMA_FROM_DEVICE

這 2 個(gè)符號應當是自解釋的. 如果數據被發(fā)向這個(gè)設備(相應地, 也許, 到一個(gè) write 系統調用), DMA_IO_DEVICE 應當被使用; 去向 CPU 的數據, 相反, 用 DMA_FROM_DEVICE 標志.

DMA_BIDIRECTIONAL

如果數據被在任一方向移動(dòng), 使用 DMA_BIDIRECTIONAL.

DMA_NONE

這個(gè)符號只作為一個(gè)調試輔助而提供. 試圖使用帶這個(gè)方向的緩沖導致內核崩潰.

可能在所有時(shí)間里試圖只使用 DMA_BIDIRECTIONAL, 但是驅動(dòng)作者應當抵擋住這個(gè)誘惑. 在一些體系上, 這個(gè)選擇會(huì )有性能損失.

當你有單個(gè)緩沖要發(fā)送, 使用 dma_map_single 來(lái)映射它:

dma_addr_t dma_map_single(struct device *dev, void *buffer, size_t size, enum dma_data_direction direction);

返回值是總線(xiàn)地址, 你可以傳遞到設備, 或者是 NULL 如果有錯誤.

一旦傳輸完成, 映射應當用 dma_unmap_single 來(lái)刪除:

void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, enum dma_data_direction direction);

這里, size 和 direction 參數必須匹配那些用來(lái)映射緩沖的.

一些重要的規則適用于流 DMA 映射:

  • 緩沖必須用在只匹配它被映射時(shí)給定的方向的傳輸.

  • 一旦一個(gè)緩沖已被映射, 它屬于這個(gè)設備, 不是處理器. 直到這個(gè)緩沖已被去映射, 驅動(dòng)不應當以任何方式觸動(dòng)它的內容. 只在調用 dma_unmap_single 后驅動(dòng)才可安全存取緩沖的內容(有一個(gè)例外, 我們馬上見(jiàn)到). 其他的事情, 這個(gè)規則隱含一個(gè)在被寫(xiě)入設備的緩沖不能被映射, 直到它包含所有的要寫(xiě)的數據.

  • 這個(gè)緩沖必須不被映射, 當 DMA 仍然激活, 否則肯定會(huì )有嚴重的系統不穩定.

你可能奇怪為什么一旦一個(gè)緩沖已被映射驅動(dòng)就不能再使用它. 為什么這個(gè)規則有意義實(shí)際上有 2 個(gè)原因. 第一, 當一個(gè)緩沖為 DMA而被映射, 內核必須確保緩沖中的所有的數據實(shí)際上已被寫(xiě)入內存. 有可能一些數據在處理器的緩存當 dma_unmap_single 被調用時(shí),并且必須被明確刷新. 被處理器在刷新后寫(xiě)入緩沖的數據可能對設備不可見(jiàn).

第二, 考慮一下會(huì )發(fā)生什么, 當被映射的緩沖在一個(gè)對設備不可存取的內存區. 一些體系在這種情況下完全失敗, 但是其他的創(chuàng )建一個(gè)反彈緩沖.反彈緩沖只是一個(gè)分開(kāi)的內存區, 它對設備可存取. 如果一個(gè)緩沖被映射使用 DMA_TO_DEVICE 方向, 并且要求一個(gè)反彈緩沖,原始緩沖的內容作為映射操作的一部分被拷貝. 明顯地, 在拷貝后的對原始緩沖的改變設備見(jiàn)不到. 類(lèi)似地, DMA_FROM_DEVICE反彈緩沖被 dma_unmap_single 拷回到原始緩沖; 來(lái)自設備的數據直到拷貝完成才出現.

偶然地, 為什么獲得正確方向是重要的, 反彈緩沖是一個(gè)原因. DMA_BIDIRECTIONAL 反彈緩沖在操作前后被拷貝, 這常常是一個(gè) CPU 周期的不必要浪費.

偶爾一個(gè)驅動(dòng)需要存取一個(gè)流 DMA 緩沖的內容而不映射它. 已提供了一個(gè)調用來(lái)做這個(gè):

void dma_sync_single_for_cpu(struct device *dev, dma_handle_t bus_addr, size_t size, enum dma_data_direction direction);

這個(gè)函數應當在處理器存取一個(gè)流 DMA 緩沖前調用. 一旦已做了這個(gè)調用, CPU "擁有" DMA 緩沖并且可以按需使用它. 在設備存取這個(gè)緩沖前, 但是, 擁有權應當傳遞回給它, 使用:

void dma_sync_single_for_device(struct device *dev, dma_handle_t bus_addr, size_t size, enum dma_data_direction direction);

處理器, 再一次, 在調用這個(gè)之后不應當存取 DMA 緩沖.

15.4.4.6. 單頁(yè)流映射

偶然地, 你可能想建立一個(gè)緩沖的映射, 這個(gè)緩沖你有一個(gè) struct page 指針; 例如, 這可能發(fā)生在使用 get_user_pages 映射用戶(hù)緩沖. 為建立和取消流映射使用 struct page 指針, 使用下面:

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);

offset 和 size 參數可被用來(lái)映射頁(yè)的部分. 但是, 建議部分頁(yè)映射應當避免, 除非你真正確信你在做什么. 映射一頁(yè)的部分可能導致緩存一致性問(wèn)題, 如果這個(gè)分配只覆蓋一個(gè)緩存線(xiàn)的一部分; 這, 隨之, 會(huì )導致內存破壞和嚴重的難以調試的錯誤.

15.4.4.7. 發(fā)散/匯聚映射

發(fā)散/匯聚映射是一個(gè)特殊類(lèi)型的流 DMA 映射. 假設你有幾個(gè)緩沖, 都需要傳送數據到或者從設備. 這個(gè)情況可來(lái)自幾個(gè)方式, 包括從一個(gè)readv 或者 writev 系統調用, 一個(gè)成簇的磁盤(pán) I/O 請求, 或者一個(gè)頁(yè)鏈表在一個(gè)被映射的內核 I/O 緩沖.你可簡(jiǎn)單地映射每個(gè)緩沖, 輪流的, 并且進(jìn)行要求的操作, 但是有幾個(gè)優(yōu)點(diǎn)來(lái)一次映射整個(gè)鏈表.

許多設備可以接收一個(gè)散布表數組指針和長(cháng)度, 并且傳送它們全部在一個(gè) DMA 操作中; 例如,"零拷貝"網(wǎng)絡(luò )是更輕松如果報文在多個(gè)片中建立. 另一個(gè)映射發(fā)散列表為一個(gè)整體的理由是利用在總線(xiàn)硬件上有映射寄存器的系統. 在這樣的系統上,物理上不連續的頁(yè)從設備的觀(guān)點(diǎn)看可被匯集為一個(gè)單個(gè)的, 連續的數組. 這個(gè)技術(shù)只當散布表中的項在長(cháng)度上等于頁(yè)大小(除了第一個(gè)和最后一個(gè)),但是當它做這個(gè)工作時(shí), 它可轉換多個(gè)操作到一個(gè)單個(gè)的 DMA, 和有針對性的加速事情.

最后, 如果一個(gè)反彈緩沖必須被使用, 應該連接整個(gè)列表為一個(gè)單個(gè)緩沖(因為它在被以任何方式拷貝).

因此現在你確信散布表的映射在某些情況下是值得的. 映射一個(gè)散布表的第一步是創(chuàng )建和填充一個(gè) struct scatterlist 數組,它描述被傳輸的緩沖. 這個(gè)結構是體系依賴(lài)的, 并且在 <asm/scatterlist.h> 中描述. 但是, 它常常包含 3個(gè)成員:

struct page *page;

struct page 指針, 對應在發(fā)散/匯聚操作中使用的緩沖.

unsigned int length;
unsigned int offset;

緩沖的長(cháng)度和它的頁(yè)內偏移.

為映射一個(gè)發(fā)散/匯聚 DMA 操作, 你的驅動(dòng)應當設置 page, offset, 和 length 成員在一個(gè) struct scatterlist 項給每個(gè)要被發(fā)送的緩沖. 接著(zhù)調用:

int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction direction)

這里 nents 是傳入的散布表項的數目. 返回值是要發(fā)送的 DMA 緩沖的數目. 它可能小于 nents.

對于輸入散布表中的每個(gè)緩沖, dma_map_sg 決定了正確的給設備的總線(xiàn)地址. 作為任務(wù)的一部分, 它也連接在內存中相近的緩沖.如果你的驅動(dòng)運行的系統有一個(gè) I/O 內存管理單元, dma_map_sg 也編程這個(gè)單元的映射寄存器, 可能的結果是, 從你的驅動(dòng)的觀(guān)點(diǎn),你能夠傳輸一個(gè)單個(gè)的, 連續的緩沖. 你將不會(huì )知道傳送的結果將看來(lái)如何, 但是, 直到在調用之后.

你的驅動(dòng)應當傳送由 pci_map_sg 返回的每個(gè)緩沖. 總線(xiàn)地址和每個(gè)緩沖的長(cháng)度存儲于 struct scatterlist 項, 但是它們在結構中的位置每個(gè)體系不同. 2 個(gè)宏定義已被定義來(lái)使得可能編寫(xiě)可移植的代碼:

dma_addr_t sg_dma_address(struct scatterlist *sg);

從這個(gè)散布表入口返回總線(xiàn)( DMA )地址.

unsigned int sg_dma_len(struct scatterlist *sg);

返回這個(gè)緩沖的長(cháng)度.

再次, 記住要傳送的緩沖的地址和長(cháng)度可能和傳遞給 dma_map_sg 的不同.

一旦傳送完成, 一個(gè) 發(fā)散/匯聚 映射被使用 dma_unmap_sg 去映射:

void dma_unmap_sg(struct device *dev, struct scatterlist *list, int nents, enum dma_data_direction direction);

注意 nents 必須是你起初傳遞給 dma_map_sg 的入口項的數目, 并且不是這個(gè)函數返回給你的 DMA 緩沖的數目.

發(fā)散/匯聚映射是流 DMA 映射, 并且同樣的存取規則如同單一映射一樣適用. 如果你必須存取一個(gè)被映射的發(fā)散/匯聚列表, 你必須首先同步它:

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);

15.4.4.8. PCI 雙地址周期映射

正常地, DMA 支持層使用 32-位 總線(xiàn)地址, 可能受限于一個(gè)特定設備的 DMA 掩碼. PCI 總線(xiàn), 但是, 也支持一個(gè)64-位地址模式, 雙地址周期(DAC). 通常的 DMA 層不支持這個(gè)模式, 因為幾個(gè)理由, 第一個(gè)是它是一個(gè) PCI-特定 的特性. 還有,許多 DAC 的實(shí)現滿(mǎn)是錯誤, 并且, 因為 DAC 慢于一個(gè)常規的, 32-位 DMA, 可能有一個(gè)性能開(kāi)銷(xiāo). 即便如此, 有的應用程序使用DAC 是正確的事情; 如果你有一個(gè)設備可能使用非常大的位于高內存的緩沖, 你可能要考慮實(shí)現 DAC 支持. 這個(gè)支持只對 PCI 總線(xiàn)適用,因此 PCI-特定的函數必須被使用.

為使用 DAC, 你的驅動(dòng)必須包含 <linux/pci.h>. 你必須設置一個(gè)單獨的 DMA 掩碼:

int pci_dac_set_dma_mask(struct pci_dev *pdev, u64 mask);

你可使用 DAC 尋址只在這個(gè)調用返回 0 時(shí). 一個(gè)特殊的類(lèi)型 (dma64_addr_t) 被用作 DAC 映射. 為建立一個(gè)這些映射, 調用 pci_dac_page_to_dma:

dma64_addr_t pci_dac_page_to_dma(struct pci_dev *pdev, struct page *page, unsigned long offset, int direction);

DAC 映射, 你將注意到, 可能被完成只從 struct page 指針(它們應當位于高內存, 畢竟, 否則使用它們沒(méi)有意義了);它們必須一次一頁(yè)地被創(chuàng )建. direction 參數是在通用 DMA 層中使用的 enum dma_data_direction 的 PCI對等體; 它應當是 PCI_DMA_TODEVICE, PCI_DMA_FROMDEVICE, 或者PCI_DMA_BIRDIRECTIONAL.

DAC 映射不要求外部資源, 因此在使用后沒(méi)有必要明確釋放它們. 但是, 有必要象對待其他流映射一樣對待 DAC 映射, 并且遵守關(guān)于緩沖所有權的規則. 有一套函數來(lái)同步 DMA 緩沖, 和通常的變體相似:

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);

15.4.4.9. 一個(gè)簡(jiǎn)單的 PCI DMA 例子

作為一個(gè) DMA 映射如何被使用的例子, 我們展示了一個(gè)簡(jiǎn)單的給一個(gè) PCI 設備的 DMA 編碼的例子. 在 PCI 總線(xiàn)上的數據的DMA 操作的形式非常依賴(lài)被驅動(dòng)的設備. 因此, 這個(gè)例子不適用于任何真實(shí)的設備; 相反, 它是一個(gè)稱(chēng)為 dad ( DMAAcquisiton Device) 的假想驅動(dòng)的一部分. 一個(gè)給這個(gè)設備的驅動(dòng)可能定義一個(gè)傳送函數象這樣:

int dad_transfer(struct dad_dev *dev, int write, void *buffer,size_t count){dma_addr_t bus_addr;/* Map the buffer for DMA */dev->dma_dir = (write ? DMA_TO_DEVICE : DMA_FROM_DEVICE);dev->dma_size = count;bus_addr = dma_map_single(&dev->pci_dev->dev, buffer, count,dev->dma_dir);dev->dma_addr = bus_addr;/* Set up the device */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));writel(dev->registers.len, cpu_to_le32(count));/* Start the operation */writeb(dev->registers.command, DAD_CMD_ENABLEDMA);return 0;}

這個(gè)函數映射要被傳送的緩沖并且啟動(dòng)設備操作. 這個(gè)工作的另一半必須在中斷服務(wù)過(guò)程中完成, 這個(gè)看來(lái)如此:

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 */dma_unmap_single(dev->pci_dev->dev, dev->dma_addr,dev->dma_size, dev->dma_dir);/* Only now is it safe to access the buffer, copy to user, etc. */...}

顯然, 這個(gè)例子缺乏大量的細節, 包括可能需要的任何步驟來(lái)阻止啟動(dòng)多個(gè)同時(shí)的 DMA 操作.

15.4.5. ISA 設備的 DMA

ISA 總線(xiàn)允許 2 類(lèi) DMA 傳送: 本地 DMA 和 ISA 總線(xiàn)主 DMA. 本地 DMA 使用在主板上的標準DMA-控制器電路來(lái)驅動(dòng) ISA 總線(xiàn)上的信號線(xiàn). ISA 總線(xiàn)主 DMA, 另一方面, 完全由外設處理, 至少從驅動(dòng)的觀(guān)點(diǎn)看. 一個(gè) ISA總線(xiàn)主的例子是 1542 SCSI 控制器, 在內核源碼中是在 drivers/scsi/aha1542.c.

至于本地 DMA, 有 3 個(gè)實(shí)體包含在 ISA 總線(xiàn)上的 DMA 數據傳送.

The 8237 DMA controller (DMAC)

控制器持有關(guān)于 DMA 傳送的信息, 諸如方向, 內存地址, 以及傳送的大小. 它還包含一個(gè)計數器來(lái)跟蹤進(jìn)行中的傳送的狀態(tài). 當這個(gè)控制器收到一個(gè) DMA 請求信號, 它獲得總線(xiàn)的控制權并且驅動(dòng)信號線(xiàn)以便設備可讀或些它的數據.

The peripheral device

這個(gè)設備必須激活 DMA 請求線(xiàn)當它準備傳送數據時(shí). 實(shí)際的傳送由 DMAC 管理; 硬件設備順序讀或寫(xiě)數據到總線(xiàn)當控制器探測設備時(shí). 設備常常觸發(fā)中斷當傳送結束時(shí).

The device driver

這個(gè)驅動(dòng)什么不做; 它提供給 DMA 控制器方向, 總線(xiàn)地址,和傳送的大小. 它還和它的外設通訊來(lái)準備傳送數據和響應中斷當 DMA 結束時(shí).

開(kāi)始的在 PC 上使用的 DMA 控制器管理 4 個(gè)"通道", 每個(gè)有一套 DMA 寄存器. 4 個(gè)設備可同時(shí)存儲它們的 DMA 信息在控制器中. 更新的 PC 包含相同的 2 個(gè) DMAC 設備[51]: 第 2 個(gè)控制器(主)被連接到系統的處理器, 并且第 1 個(gè)(從)被連接到第 2 個(gè)控制器的通道 0.

最初的 PC 只有一個(gè)控制器; 第 2 個(gè)是在基于 286 的平臺上增加的. 但是, 第 2 個(gè)控制器如同主控制器一樣被連接, 因為它處理 16-位的傳送; 第 1 個(gè)只傳送 8 位每次并且它為向后兼容而存在.

通道的編號從 0 到 7: 通道 4 對 ISA 外設不可用, 因為它在內部用來(lái)層疊從控制器到主控制器. 因此, 可用的通道是 0 到 3在從控制器上( 8-位 通道) 和 5 到 7 到主控制器上( 16-位通道). 任何 DMA 傳送的大小, 當被存儲于控制器中,是一個(gè)代表總線(xiàn)周期的數目的 16-位數. 最大的傳送大小是, 因此, 64KB 對于從控制器(因為它傳送 8 位在一個(gè)周期)和 128KB對于主控制器( 它進(jìn)行 16-位 傳送).

因為 DMA 控制器是一個(gè)系統范圍的資源, 內核幫助處理這個(gè). 它使用一個(gè) DMA 注冊來(lái)提供一個(gè)請求并釋放機制給 DMA 通道, 和一套函數來(lái)在 DMA 控制器中配置通道信息.

15.4.5.1. 注冊 DMA 使用

你應當熟悉內核注冊 -- 我們已經(jīng)見(jiàn)到它們在 I/O 端口和中斷線(xiàn). DMA 通道注冊和其他的類(lèi)似. 在 <asm/dma.h> 中已經(jīng)包含, 下面的函數可用來(lái)獲得和釋放一個(gè) DMA 通道的擁有權:

int request_dma(unsigned int channel, const char *name);void free_dma(unsigned int channel);

通道參數是一個(gè)在 0 到 7 之間的數, 更精確些, 一個(gè)小于 MAX_DMA_CHANNELS 的正值. 在 PC 上,MAX_DMA_CHANNELS 定義為 8 來(lái)匹配硬件. name 參數是一個(gè)字符串來(lái)標識設備. 特定的 name 出現在文件/proc/dma, 它可被用戶(hù)程序讀.

從 request_dma 的返回值是 0 對于成功, 是 -EINVAL 或者 -EBUSY 如果有錯誤. 前者意思是請求的通道超范圍, 后者意思是另一個(gè)設備持有這個(gè)通道.

我們推薦你象對待 I/O 端口和中斷線(xiàn)一樣小心對待 DMA 通道; 在打開(kāi)時(shí)請求通道好于從模塊初始化函數里請求它. 延后請求允許在驅動(dòng)之間的一些共享; 例如, 你的聲卡和模擬 I/O 接口可以共享 DMA 通道只要它們不同時(shí)使用.

我們還建議你請求 DMA 通道在你已請求中斷線(xiàn)之后并且你在中斷前釋放它. 這是慣用的順序來(lái)請求這 2 個(gè)資源; 遵循這個(gè)慣例避免了死鎖的可能. 注意每個(gè)使用 DMA 的設備需要一個(gè) IRQ 線(xiàn); 否則, 它不能指示數據傳送的完成.

在一個(gè)典型的情況, open 代碼看來(lái)如下, 引用了我們的假想的 dad 模塊. dad 設備使用了一個(gè)快速中斷處理, 不帶共享 IRQ 線(xiàn)支持.

int dad_open (struct inode *inode, struct file *filp){struct dad_device *my_device;/* ... */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 實(shí)現看來(lái)如此:

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);/* ... */}

這是 /proc/dma 文件 在一個(gè)安裝有聲卡的系統中的樣子:

merlino% cat /proc/dma1: Sound Blaster84: cascade

注意, 缺省的聲音驅動(dòng)獲得 DMA 通道在系統啟動(dòng)時(shí)并且從不釋放它. 層疊的入口是一個(gè)占位者, 指出通道 4 對驅動(dòng)不可用, 如同前面解釋的.

15.4.5.2. 和 DMA 控制器通訊

在注冊后, 驅動(dòng)工作的主要部分包括配置 DMA 控制器正確操作. 這個(gè)任務(wù)并非微不足道的, 但是幸運的是, 內核輸出了典型驅動(dòng)需要的所有的函數.

驅動(dòng)需要配置 DMA 控制器或者讀或寫(xiě)被調用時(shí), 或者當準備異步傳送時(shí). 后面這個(gè)任務(wù)或者在打開(kāi)時(shí)進(jìn)行或者響應一個(gè) ioctl 命令, 根據驅動(dòng)和它實(shí)現的策略. 這里展示的代碼是典型地被讀或寫(xiě)設備方法調用的.

這一小節提供一個(gè)對于 DMA 控制器內部的快速概覽, 這樣你可理解這里介紹的代碼. 如果你想知道更多, 我們勸你讀<asm/dma.h> 和一些描述 PC 體系的硬件手冊. 特別地, 我們不處理 8-位 和 16-位 傳送的問(wèn)題.如果你在編寫(xiě)設備驅動(dòng)給 ISA 設備板, 你應當在設備的硬件手冊中找到相關(guān)的信息.

DMA 控制器是一個(gè)共享的資源, 并且如果多個(gè)處理器試圖同時(shí)對它編程會(huì )引起混亂. 為此, 控制器被一個(gè)自旋鎖保護, 稱(chēng)為 dma_spin_lock. 驅動(dòng)不應當直接操作這個(gè)鎖; 但是, 2 個(gè)函數已提供給你來(lái)做這個(gè):

unsigned long claim_dma_lock( );

獲取 DMA 自旋鎖. 這個(gè)函數還在本地處理器上阻塞中斷; 因此, 返回值是一些描述之前中斷狀態(tài)的標志; 它必須被傳遞給隨后的函數來(lái)恢復中斷狀態(tài), 當你用完這個(gè)鎖.

void release_dma_lock(unsigned long flags);

返回 DMA 自旋鎖并且恢復前面的中斷狀態(tài).

自旋鎖應當被持有, 當使用下面描述的函數時(shí). 但是, 它不應當被持有, 在實(shí)際的 I/O 當中. 一個(gè)驅動(dòng)應當從不睡眠當持有一個(gè)自旋鎖時(shí).

必須被加載到控制器中的信息包括 3 項: RAM 地址, 必須被傳送的原子項的數目(以字節或字計), 以及傳送的方向. 為此, 下列函數由 <asm/dma.h> 輸出:

void set_dma_mode(unsigned int channel, char mode);

指示是否這個(gè)通道必須從設備讀( DMA_MODE_READ)或者寫(xiě)到設備(DMA_MODE_WRITE). 存在第 3 個(gè)模式,DMA_MODE_CASCADE, 它被用來(lái)釋放對總線(xiàn)的控制. 層疊是第 1 個(gè)控制器連接到第 2 個(gè)控制器頂部的方式, 但是它也可以被真正的ISA 總線(xiàn)主設備使用. 我們這里不討論總線(xiàn)控制.

void set_dma_addr(unsigned int channel, unsigned int addr);

分配 DMA 緩沖的地址. 這個(gè)函數存儲 addr 的低 24 有效位在控制器中. addr 參數必須是一個(gè)總線(xiàn)地址(見(jiàn)"總線(xiàn)地址"一節, 在本章前面).

void set_dma_count(unsigned int channel, unsigned int count);

分配傳送的字節數. count 參數也表示給 16-位 通道的字節; 在這個(gè)情況下, 這個(gè)數必須是偶數.

除了這些函數, 有一些維護工具必須用, 當處理 DMA 設備時(shí):

void disable_dma(unsigned int channel);

一個(gè) DMA 通道可在控制器內部被關(guān)閉. 這個(gè)通道應當在控制器被配置為阻止進(jìn)一步不正確的操作前被關(guān)閉. (否則, 會(huì )因為控制器被通過(guò) 8-位數據傳送被編程而發(fā)生破壞, 并且, 因此, 之前的功能都不自動(dòng)執行.

void enable_dma(unsigned int channel);

這個(gè)函數告知控制器 DMA 通道包含有效數據.

int get_dma_residue(unsigned int channel);

這個(gè)驅動(dòng)有時(shí)需要知道是否一個(gè) DMA 傳輸已經(jīng)完成. 這個(gè)函數返回仍要被傳送的字節數. 在一次成功的傳送后的返回值是 0 并且在控制器在工作時(shí)是不可預測的 (但不是 0). 這種不可預測性來(lái)自需要通過(guò) 2 個(gè)8-位輸入操作來(lái)獲得 16-位 的余數.

void clear_dma_ff(unsigned int channel) ;

這個(gè)函數清理 DMA flip-flop. 這個(gè) flip-flop 用來(lái)控制對 16-位 寄存器的存取. 這些寄存器被 2個(gè)連續的 8-位操作來(lái)存取, 并且這個(gè) flip-flop 被用來(lái)選擇低有效字節(當它被清零)或者是最高有效字節(當它被置位).flip-flop 自動(dòng)翻轉當已經(jīng)傳送了 8 位; 程序員必須清除 flip-flop( 來(lái)設置它為已知的狀態(tài) )在存取 DMA 寄存器之前.

使用這些, 一個(gè)驅動(dòng)可如下實(shí)現一個(gè)函數來(lái)準備一次 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;}

接著(zhù), 一個(gè)象下一個(gè)的函數被用來(lái)檢查 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);}

未完成的唯一一個(gè)事情是配置設備板. 這個(gè)設備特定的任務(wù)常常包含讀或寫(xiě)幾個(gè) I/O 端口. 設備在幾個(gè)大的方面不同. 例如, 一些設備期望程序員告訴硬件 DMA 緩沖有多大, 并且有時(shí)驅動(dòng)不得不讀一個(gè)被硬連到設備中的值. 為配置板, 硬件手冊是你唯一的朋友.

本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
DMA映射
PCI設備的DMA映射操作詳解
Dynamic DMA mapping Guide
Linux內核DMA機制 - ShangShuWu
LINUX DEVICE 第三版(快速參考)
Linux2.6內核驅動(dòng)移植2
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久