標籤:defer 批量 fse element 資料 相關 個人 映射 1.4
近期有些人問我PCI裝置驅動的問題, 和他們交流過後, 我建議他們先看一看<<The Windows NT Device Driver Book>>這本書, 個人感覺, 這本書寫得很連貫流暢.
PCI裝置驅動基本包含了PCI的資源擷取, 配置空間的讀寫, 中斷的處理, 中斷後半部在DPC中的處理.
同一時候, 也必須瞭解DMA, ScatterGater, MapRegister, Common Buffer等基礎.
1.1 PCI裝置資源擷取
PCI裝置的資源是系統依據裝置的屬性(配置空間中寄存器的值)來動態分配的.
驅動中僅僅需在PNP START中擷取這些系統分配的資源:
比如: 筆者開發的PCI電視卡驅動中, 就使用到了當中了兩類資源, CmResourceTypePort與CmResourceTypeInterrupt.
Port地址作為裝置寄存器首地址, 之後, 能夠使用WRITE_PORT_ULONG與READ_PORT_ULONG加上對應的OFFSET來對裝置寄存器進行訪問.
Interrupt資源中解釋出來的內容, 則主要作為IoConnectInterrupt系統函數的參數, 將裝置的硬體中斷與ISR相關聯, KINTERRUPT的執行個體則是裝置中斷的軟體形式的載體.
1.2 DMA
DMA裝置, 在系統中分為MASTER與SLAVE, 另外一個非常重要的能力就是是否支援Scatter/Gather.
這些能力終於表如今DEVICE_DESCRIPTION所定義的資料結構的成員中, 比如:DmaWidth, ScatterGather, Master, Dma32BitAddresses, Dma64BitAddresses.
系統終於將各種不同類型的裝置DMA抽象為DMA_ADAPTER的執行個體, 它是裝置DMA軟體形式的載體.
驅動代碼通過IoGetDmaAdapter系統調用, 將物理裝置對象PDO與DMA描寫敘述結構作為參數, 終於得到這個DMA_ADAPTER對象, 作為興許一系列DMA相關操作的實體物件.
1.3 Map Register
使用者空間, 核心空間的虛擬記憶體與實體記憶體的關聯是通過頁表來映射的, 驅動程式猿經常會使用MDL, 它也是某一特定地區虛擬記憶體與實體記憶體的映射關係.
而DMA裝置則須要從匯流排地址(MSDN中又叫邏輯地址)與記憶體物理的映射關係角度去看待系統記憶體.
這個映射的關係就是由Map Register承擔的.
只是, 這批Map Register則依據系統而定, 有些是硬體實現, 有些是軟體中劃分出來的特定的一塊記憶體.
IoGetDmaAdapter的調用, 也是向系統申請Map Register的過程.
1.4 Common Buffer
這也是大家問得最多的問題
簡單地講, Common buffer是以DMA_ADAPTER為代表所申請的, 申請成功後, 既能通過虛擬位址訪問, 也能夠通過DMA控制器所屬邏輯地址空間的地址來訪問的連續實體記憶體.
它的優點就是物理上連續, 存在的問題是系統中連續實體記憶體是隨著系統的執行時間的流逝, 越來越稀缺.
AllocateCommonBuffer系統調用是作為DMA_ADAPTER的DmaOperations形式存在的, 所以, 詳細的一塊Common Buffer能夠說, 是與詳細的一個DMA控制器所關聯的.
AllocateCommonBuffer成功調用後, 會返回虛擬位址與DMA控制器所屬邏輯空間的邏輯地址.
筆者開發的PCI電視卡, 就是通過AllocateCommonBuffer分配一塊較小的連續實體記憶體, 用來存放Scatter/Gather列表 (某塊記憶體的邏輯地址SCATTER_GATHER_LIST.Elements[i].Address.LowPart 與該記憶體的長度SCATTER_GATHER_LIST.Elements[i].Length, 對應操作通過common buffer的虛擬位址 ).
這個Scatter/Gather List列表終於由具有S/G能力的DMA控制器來讀取(對應操作通過common buffer的邏輯地址), 依據當中的表項, 進行DMA讀/寫操作.
1.5 S/G
S/G的能力是DMA控制器的特性, 假設具有S/G的能力, 則能夠批量地DMA操作, 否則, 必須一次一次地使用MapTransfer來完畢DMA操作.
系統空間的中虛擬記憶體與實體記憶體之間的聯絡通過IoAllocateMdl與MmBuildMdlForNonPagedPool建立特定的MDL來表示.
其後,通過DMA_ADAPTER的DmaOperations中的GetScatterGatherList擷取MDL所描寫敘述的虛擬位址記憶體的S/G列表, 最後, 在GetScatterGatherList的
ExecutionRoutine 函數中, 將該列表填入Common buffer的TABLE(起始邏輯地址 與 長度)中, 以供DMA Controller所用.
1.6 ISR與DPC
剛才已經提到, ISR是通過IoConnectInterrupt注冊的.
ISR在裝置中斷到來時實調用, 但詳細的事項則交由(KeInsertQueueDpc)DPC來處理.
而DPC則是通過KeInitializeDpc系統調用, 將DPC對象KDPC與詳細的KDEFERRED_ROUTINE DPC處理函數相關聯的.
1.7 PCI裝置配置空間的訪問
其實, 普通情況下, Windows PCI裝置並不須要訪問PCI裝置配置空間.
但作為一個完整的PCI裝置驅動, 這裡提及一下.
因為PCI裝置的配置空間與IO/MEM空間是分開的, 前面已經提及IO/MEM的訪問方式, 配置空間的訪問例如以下:
定義變數:BUS_INTERFACE_STANDARD m_BusInterfaceStandard;
建立: IRP, 主與次分別為IRP_MJ_PNP, IRP_MN_QUERY_INTERFACE, 得到BUS_INTERFACE_STANDARD資料結構.
之後, 通過BUS_INTERFACE_STANDARD中的SetBusData與GetBusData來進行PCI配置空間的寄存器讀寫.
PCI裝置驅動全然能夠用在PCIe裝置上, 畢竟上層來講, 他們沒有太多的差別.
與USB驅動不同, PCI裝置須要考慮驅動設計中的方方面面, 希望這篇文章對大家有所借鑒作用.
第二十七篇:Windows驅動中的PCI, DMA, ISR, DPC, ScatterGater, MapRegsiter, CommonBuffer, ConfigSpace