原文出處:http://www.cnblogs.com/jacklu/p/4679304.html
如果你覺得這篇博客對你的項目有用,請引用以下論文:
Meng Shengwei, Lu Jianjie. Design of a PCIe Interface Card Control Software Based on WDF. Fifth International Conference on Instrumentation and Measurement, Computer, Communication and Control. IEEE, 2016:767-770.
在WDF的PCIe驅動程序中,共有四個.h文件(Public.h Driver.h Device.h Trace.h)。本文將分別對四個文件部分源代碼進行詳細的解釋。
Public.h
1 #ifndef _USER_H 2 #define _USER_H 3 // 4 // Define an Interface Guid so that app can find the device and talk to it. 5 // 6 #include <initguid.h> 7 // {49FA63A7-C525-4409-8DD5-EFF37A7375F8} 8 DEFINE_GUID(GUID_DEVINTERFACE_Spw_PCIe, 9 0x49fa63a7, 0xc525, 0x4409, 0x8d, 0xd5, 0xef, 0xf3, 0x7a, 0x73, 0x75, 0xf8); 10 #define Spw_PCIe_IOCTL_IN_BUFFERED CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)//the least value is 0x800 11 #define Spw_PCIe_IOCTL_OUT_BUFFERED CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS) 12 #define Spw_PCIe_IOCTL_READ_PADDRESS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS) 13 #define Spw_PCIe_IOCTL_WRITE_OFFSETADDRESS CTL_CODE(FILE_DEVICE_UNKNOWN, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS) 14 #endif
代碼文件名為Public,是因為這個文件要被驅動程序和應用程序共同使用的。在第4行的注釋里,寫明了本文件的主要作用是為驅動程序和應用程序的通信提供GUID接口。在Windows平台下實現對硬件設備的控制,需要應用程序能夠與底層驅動進行通信,應用程序與驅動通信的設計過程中有兩個重要的概念,即GUID值和CTL_CODE宏。
GUID(Globally Unique Identifier)是微軟推出的全局唯一標識符,通過使用某個特定的算法(比如根據時間或地點等信息)生成一組128位二進制數,來標識某一個實體,比如硬盤上的一張圖片。GUID廣泛應用於微軟的產品中,用於識別接口、文件等對象。開發者可以使用VS2013下的工具GUIDGen.exe生成GUID值,該GUID標識驅動程序,應用程序根據這個GUID值來找到對應的驅動程序。注意使用DEFINE_GUID宏,要包含initguid.h文件,否則會報出無法識別的error。
I/O處理例程DeviceIoControl(這個例程將在下一篇文章中詳細介紹)的第二個參數dwIoControlCode就是由CTL_CODE宏定義的。CTL_CODE是一個用於創建一個唯一的32位系統I/O控制代碼的宏,這個控制代碼包括4部分組成:DeviceType(設備類型,高16位(16-31位)),Access(訪問限制,14-15位),Function(功能2-13 位),Method(I/O訪問內存使用方式)。CTL_CODE定義中有一個Method域,該域定義了驅動程序中獲取應用程序數據緩沖區地址的方式。
10-13行代碼為用戶自定義的4個I/O控制命令,分別為讀數據、寫數據、讀映射的BAR0的物理起始地址、寫偏移地址(用來讀寫數據)。
第一個參數為設備類型,通常為自定義的板卡選擇FILE_DEVICE_UNKNOWN;
注意,第二個數字要從0x800開始取值,之前的已經被微軟占用;
第三個參數有四種選擇(METHOD_BUFFERED、METHOD_IN_DIRECT、METHOD_OUT_DIRECT、METHOD_NEITHER),buffered方式:I/O管理器會創建與應用程序數據緩沖區完全相同的系統緩沖區,驅動程序在這個緩沖區工作,由I/O管理器完成復制數據任務;direct方式:I/O管理器鎖定應用程序緩沖區的物理內存頁,並創建一個MDL(內存描述符表)來描述該頁,驅動程序將使用MDL工作;neither方式:I/O管理器把應用程序緩沖區的虛擬地址傳遞給驅動程序,一般不采用這種方式。
第四個參數用來設置文件讀寫權限,有三種選擇(FILE_ANY_ACCESS、FILE_READ_ACCESS、FILE_WRITE_ACCESS),微軟官方的說法是“The FILE_ACCESS_ANY is generally the correct value.”,所以我們還是老實的選用FILE_ACCESS吧。
Driver.h
1 #define INITGUID 2 #pragma warning(disable:4200) // 3 #pragma warning(disable:4201) // nameless struct/union 4 #pragma warning(disable:4214) // bit field types other than int 5 6 #include <ntddk.h> 7 #include <wdf.h> 8 9 #include "Public.h" 10 #include "device.h" 11 #include "trace.h"
這個文件將基於WDF的PCIe驅動程序所需要的頭文件都包含在了一起,並且disable了一些警告,由於驅動程序的開發非常注意warnning的處理,VS2013下是默認有warnning的時候不通過編譯的,需要開發者手動設置一下。對於一些無關痛癢的warnning,我們也可以通過預處理的方式來disable它。
Device.h
1 #include "public.h" 2 3 #define PCIE_WRITE_MEMORY_OFFSET 0x20000//memory1's offset address to BAR0 in FPGA 4 #define PCIE_READ_MEMORY_OFFSET 0x22000//memory2's offset address to BAR0 in FPGA 5 6 #define MAXNLEN 1024 //define the largest length 7 // 8 //device context is same as device extension in WDM 9 // 10 typedef struct _DEVICE_CONTEXT 11 { 12 ULONG Counter_i;//counter for WdfCmResourceListGetCount(ResourceListTranslated) 13 PVOID MemBaseAddress;//when i == 5,it gets BAR2 start virtual address 14 PVOID BAR0_VirtualAddress;//BAR0 start virtual address 15 ULONG PhysicalAddressRegister;//store the BAR0 start physical address 16 ULONG MemLength;//it records the length of menmory on hardware 17 ULONG OffsetAddressFromApp;//get offset address that is given by application 18 } DEVICE_CONTEXT, *PDEVICE_CONTEXT; 19 20 // 21 // This macro will generate an inline function called DeviceGetContext 22 // which will be used to get a pointer to the device context memory 23 // in a type safe manner. 24 // 25 //WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_CONTEXT, DeviceGetContext) 26 WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_CONTEXT, GetDeviceContext)//very important! 27 28 // 29 // WDFDRIVER Events including "EVT" 30 // 31 DRIVER_INITIALIZE DriverEntry; 32 EVT_WDF_DRIVER_DEVICE_ADD Spw_PCIeEvtDeviceAdd; 33 EVT_WDF_OBJECT_CONTEXT_CLEANUP Spw_PCIeEvtDriverContextCleanup; 34 35 EVT_WDF_DEVICE_D0_ENTRY Spw_PCIeEvtDeviceD0Entry; 36 EVT_WDF_DEVICE_D0_EXIT Spw_PCIeEvtDeviceD0Exit; 37 EVT_WDF_DEVICE_PREPARE_HARDWARE Spw_PCIeEvtDevicePrepareHardware; 38 EVT_WDF_DEVICE_RELEASE_HARDWARE Spw_PCIeEvtDeviceReleaseHardware; 39 40 EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL Spw_PCIeEvtIoDeviceControl;
這個文件定義了與設備和驅動程序密切相關的一些東西,非常重要。
3-4行定義了兩個宏,即設置了兩塊內存在BAR0映射的偏移地址0x20000和0x22000,這兩個值與PCIe硬件板卡有關。在讀寫操作時,BAR0的物理地址一定要加上偏移地址,否則會因為寫到未知內存單元而造成藍屏等后果。
6行定義了最大傳輸的大小,在以后DMA操作中會用到。
10-18行定義了與設備相關的變量,把這些變量參數封裝在一個結構體中,體現WDF中的一種“對象封裝”的思想。幾個參數分別表示資源計數器(記錄WDF框架分配給設備的資源個數)、記錄BAR2的起始地址(當資源計數器 i == 0 時)、BAR0經過轉換后的虛擬地址(可被應用程序使用)、BAR0映射的起始地址(物理地址,與設備管理器中所獲得結果相同)、內存大小、偏移地址(由應用程序傳遞過來,供I/O讀取使用)。在WDF中這個結構體被叫做設備上下文(DEVICE_CONTEXT),在WDM中叫做DEVICE_EXTENSION
26行的宏非常重要,具體作用將在下一篇介紹源文件中詳細說明。
31-40行聲明了一些WDF事件回調例程,聲明后,我們可以直接使用自定義的回調函數名,這些聲明的作用只起到開發者方便編寫程序的作用。
DriverEntry為驅動程序入口函數;
Spw_PCIeEvtDeviceAdd為設備添加函數,非常重要,要自己說三遍;
Spw_PCIeEvtDriverContextCleanup為資源清理函數,由於操作系統越來越智能,當設備被拔出后,操作系統會自動回收一些資源,所以這個函數貌似在PCIe驅動里並沒什么卵用,即使我在這做了聲明,在下一篇介紹源代碼中,我們也會發現,它的函數定義也只“打了一個醬油”;
Spw_PCIeEvtDeviceD0Entry和Spw_PCIeEvtDeviceD0Exit是與電源管理相關的兩個函數,WDF已經將電源管理做了很好的封裝,一般不需要驅動程序開發者在作處理,所以它們兩個也是“打醬油”的;
Spw_PCIeEvtDevicePrepareHardware和Spw_PCIeEvtDeviceReleaseHardware非常重要!!!自己說三遍,分別為設備獲取資源和釋放資源;
Spw_PCIeEvtIoDeviceControl是實現應用程序與驅動程序通信的函數,里面規定了不同的控制碼實現不同的操作。
在這一部分,先對這幾個例程做簡要概述,詳細的解釋將會在下一篇給出。
還有最后一個頭文件,用來調試和跟蹤,由於我也沒用到調試和跟蹤驅動程序,所以直接給出VS2013+WDK8.1自動生成的代碼文件,不做解釋了。
trace.h
1 /*++ 2 3 Module Name: 4 5 Trace.h 6 7 Abstract: 8 9 Header file for the debug tracing related function defintions and macros. 10 11 Environment: 12 13 Kernel mode 14 15 --*/ 16 17 // 18 // Define the tracing flags. 19 // 20 // Tracing GUID - ad7b826c-e901-457e-a559-a221404519c6 21 // 22 23 #define WPP_CONTROL_GUIDS \ 24 WPP_DEFINE_CONTROL_GUID( \ 25 Spw_PCIeTraceGuid, (ad7b826c,e901,457e,a559,a221404519c6), \ 26 \ 27 WPP_DEFINE_BIT(MYDRIVER_ALL_INFO) \ 28 WPP_DEFINE_BIT(TRACE_DRIVER) \ 29 WPP_DEFINE_BIT(TRACE_DEVICE) \ 30 WPP_DEFINE_BIT(TRACE_QUEUE) \ 31 ) 32 33 #define WPP_FLAG_LEVEL_LOGGER(flag, level) \ 34 WPP_LEVEL_LOGGER(flag) 35 36 #define WPP_FLAG_LEVEL_ENABLED(flag, level) \ 37 (WPP_LEVEL_ENABLED(flag) && \ 38 WPP_CONTROL(WPP_BIT_ ## flag).Level >= level) 39 40 #define WPP_LEVEL_FLAGS_LOGGER(lvl,flags) \ 41 WPP_LEVEL_LOGGER(flags) 42 43 #define WPP_LEVEL_FLAGS_ENABLED(lvl, flags) \ 44 (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= lvl) 45 46 // 47 // This comment block is scanned by the trace preprocessor to define our 48 // Trace function. 49 // 50 // begin_wpp config 51 // FUNC Trace{FLAG=MYDRIVER_ALL_INFO}(LEVEL, MSG, ...); 52 // FUNC TraceEvents(LEVEL, FLAGS, MSG, ...); 53 // end_wpp 54 //
廣告時間~本人博士賺外快,如需要完整的驅動程序源代碼請聯系合作email: 346457821@qq.com