驅動程式 - Kernel Mode Driver Framework (KMDF) - 教學說明 - 4. User Application透過File跟驅動程式溝通



基本上,驅動程式(韌體)的功能就是做為User Application(軟體)和硬體的溝通橋樑,User Application可以透過很多種方式和驅動程式溝通,而最常用的溝通方式就是把驅動程式當做一個檔案來操作,User Application可以透過檔案讀寫的方式和驅動程式做資料交換,最後,驅動程式再把資料讀寫到硬體裝置上,而在Windows系統上,可以使用的Win32 API有CreateFile(檔案開啟)、ReadFile(讀取檔案)、WriteFile(寫入檔案)、CloseHandle(關閉檔案)等檔案操作的API,必須記得open()並不能用來開啟驅動程式,那User Application和驅動程式的串接過程是如何發生的呢?首先,驅動程式必須在DriverEntry()設定EvtDeviceFileCreate(CreateFile)、EvtIoRead(ReadFile)、EvtIoWrite(WriteFile)、EvtFileClose(CloseHandle)的Callback副程式,接著驅動程式必須設定裝置要使用的名稱,可以是GUID或者Symbolic Link格式,最後,當User Application透過檔案系統開啟這個GUID或者Symbolic Link名稱時,I/O Manager就會幫忙串接到對應的驅動程式,此後,User Application就可以透過開啟的檔案Handle開始和驅動程式做資料交換

操作步驟可以總結如下:
1. 安裝驅動程式時,驅動程式註冊檔案操作的Callback副程式
2. 驅動程式產生自己的Device Object並且設定GUID或Symbolic Link名稱
2. User Application開啟檔案(GUID或Symbolic Link名稱)時,I/O Manager會呼叫對應的驅動程式EvtDeviceFileCreate
3. User Application讀寫檔案時,I/O Manager會呼叫驅動程式的EvtIoRead、EvtIoWrite
4. User Application關閉檔案時,I/O Manager會呼叫驅動程式的EvtFileClose

使用者可能好奇想問,為何User Application不直接跟硬體溝通就好,還要透過一個這麼複雜的驅動程式當作溝通橋樑呢?這是因為,當User Application可以直接跟硬體溝通時,會造成很多衝突以及訪問的優先順序問題,就像MS-DOS時代的混亂情形,而因為可以直接控制硬體,因此,寫出一個非惡意但具有破壞性的軟體時,是間接可以把一個操作系統搞壞掉,這也是為何要在Windows 98之後(正確來說,應該是MS-DOS時代就有),架構出一個Windows驅動程式的原因,至少有一個溝通和協調的機制,當然,隨著Windows系統的發展,驅動程式的規定就會變得越來嚴格,這也是Microsoft致力於保護系統的最好方式

對應的溝通管道

Win32 API Kernel Callback
CreateFile() EvtDeviceFileCreate()
ReadFile() EvtIoRead()
WriteFile() EvtIoWrite()
CloseHandle() EvtFileClose()

驅動程式對應的CreateFile()和CloseHandle() Callback必須使用WDF_FILEOBJECT_CONFIG_INIT()設定,宣告如下:

void WDF_FILEOBJECT_CONFIG_INIT(
    PWDF_FILEOBJECT_CONFIG     FileEventCallbacks,
    PFN_WDF_DEVICE_FILE_CREATE EvtDeviceFileCreate,
    PFN_WDF_FILE_CLOSE         EvtFileClose,
    PFN_WDF_FILE_CLEANUP       EvtFileCleanup
);

ReadFile()和WriteFile() Callback則位於WDF_IO_QUEUE_CONFIG(EvtIoRead和EvtIoWrite)

typedef struct _WDF_IO_QUEUE_CONFIG {
    ULONG                                       Size;
    WDF_IO_QUEUE_DISPATCH_TYPE                  DispatchType;
    WDF_TRI_STATE                               PowerManaged;
    BOOLEAN                                     AllowZeroLengthRequests;
    BOOLEAN                                     DefaultQueue;
    PFN_WDF_IO_QUEUE_IO_DEFAULT                 EvtIoDefault;
    PFN_WDF_IO_QUEUE_IO_READ                    EvtIoRead;
    PFN_WDF_IO_QUEUE_IO_WRITE                   EvtIoWrite;
    PFN_WDF_IO_QUEUE_IO_DEVICE_CONTROL          EvtIoDeviceControl;
    PFN_WDF_IO_QUEUE_IO_INTERNAL_DEVICE_CONTROL EvtIoInternalDeviceControl;
    PFN_WDF_IO_QUEUE_IO_STOP                    EvtIoStop;
    PFN_WDF_IO_QUEUE_IO_RESUME                  EvtIoResume;
    PFN_WDF_IO_QUEUE_IO_CANCELED_ON_QUEUE       EvtIoCanceledOnQueue;
    union {
        struct {
            ULONG NumberOfPresentedRequests;
        } Parallel;
    } Settings;
    WDFDRIVER                                   Driver;
} WDF_IO_QUEUE_CONFIG, *PWDF_IO_QUEUE_CONFIG;