WDF驅動開發- CONTEXT和IO QUEUE

https://blog.csdn.net/zj510/article/details/16987349

WDF裏面,大多數對象都支持自定義的數據,比如給設備對象創建一個context。

對象上下文

先自定義一個結構,比如

typedef struct
{
    WDFQUEUE _DefaultQueue;
}DEVICE_CONTEXT;
裏面放了一個對象WDFQUEUE. 然後給設備對象創建一個上下文內存塊。在使用之前先要聲明一下這個結構,相當於告訴框架,我們需要使用一個context。

WDF_DECLARE_CONTEXT_TYPE(DEVICE_CONTEXT);
如果不需要context,那麼可以這麼初始化一個設備屬性,WDF_OBJECT_ATTRIBUTES_INIT(&object_attribs);

如果需要context,那麼要換個函數,WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&object_attribs, DEVICE_CONTEXT); 這樣就會分配一個DEVICE_CONTEXT的內存塊,並且將內存塊的指針保存到WDF_OBJECT_ATTRIBUTES裏面。看一下WDF_OBJECT_ATTRIBUTES的定義:

typedef struct _WDF_OBJECT_ATTRIBUTES {
  ULONG                          Size;
  PFN_WDF_OBJECT_CONTEXT_CLEANUP EvtCleanupCallback;
  PFN_WDF_OBJECT_CONTEXT_DESTROY EvtDestroyCallback;
  WDF_EXECUTION_LEVEL            ExecutionLevel;
  WDF_SYNCHRONIZATION_SCOPE      SynchronizationScope;
  WDFOBJECT                      ParentObject;
  size_t                         ContextSizeOverride;
  PCWDF_OBJECT_CONTEXT_TYPE_INFO ContextTypeInfo;
} WDF_OBJECT_ATTRIBUTES, *PWDF_OBJECT_ATTRIBUTES;
最後一個成員ContextTypeInfo就指向分配出來的context內存塊。

可以根據對象來得到這個context:

    dev_ctx = WdfObjectGetTypedContext(control_device, DEVICE_CONTEXT);
    RtlZeroMemory(dev_ctx, sizeof(DEVICE_CONTEXT));
上面2行代碼的意思就是獲取設備對象control_device的上下文內存塊,並且清零。

 

IO QUEUE,存放WDFREQUEST的隊列

WDF提供了一個對象WDFREQUEST, WDFREQUEST其實是對WDM裏面IRP的一個封裝。WDF還提供了另外一個對象也就是WDFQUEUE,這個隊列會存放IRP請求。

我們可以給設備對象control_device創建一個IOQUEUE,比如:(WDFQUEUE存放在了設備對象的上下文裏面)

默認隊列不能使用WdfIoQueueDispatchManual,不然創建queue會失敗。這個我試過。

    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&qcfg, WdfIoQueueDispatchParallel);    
    qcfg.PowerManaged = WdfFalse;  //非pnp驅動,也就無需電源管理了.
    qcfg.EvtIoDefault = EvtIoPDPControlDevice; 
 
    //創建一個queue
    status = WdfIoQueueCreate(control_device,&qcfg,WDF_NO_OBJECT_ATTRIBUTES,&dev_ctx->_DefaultQueue);  
    if( !NT_SUCCESS(status) )  
    {     
        KdPrint(("Create IoQueue failed, %x\n", status));
        goto DriverEntry_Complete;    
    }
注意, 需要在WdfControlFinishInitializing(control_device);之前創建IOQUEUE. 我試過在WdfControlFinishInitializing(control_device);後面創建,會失敗。

在這個例子裏面,我們提供了一個EvtIoDefault的回調函數,簡單實現一下,就是打印一下WDFREQUEST的type並且完成這個請求。

static VOID EvtIoPDPControlDevice( WDFQUEUE Queue , WDFREQUEST Request )
{
    //
    WDF_REQUEST_PARAMETERS Params;  
    WDF_REQUEST_PARAMETERS_INIT(&Params);  
    WdfRequestGetParameters(Request,&Params);
 
    KdPrint(("EvtIoPDPControlDevice, type: %x\n", Params.Type));
    
    WdfRequestComplete(Request, STATUS_SUCCESS);
 
}
 

完整代碼:

#include <fltKernel.h>
#include <wdf.h>
#include <wdfdriver.h>
#include <wdfrequest.h>
 
#define MYWDF_KDEVICE L"\\Device\\MyWDF_Device"//設備名稱,其他內核模式下的驅動可以使用
#define MYWDF_LINKNAME L"\\DosDevices\\MyWDF_LINK"//符號連接,這樣用戶模式下的程序可以使用這個驅動設備。
 
typedef struct
{
    WDFQUEUE _DefaultQueue;
}DEVICE_CONTEXT;
 
WDF_DECLARE_CONTEXT_TYPE(DEVICE_CONTEXT);
 
 
//聲明回調
EVT_WDF_DRIVER_UNLOAD EvtDriverUnload;
EVT_WDF_DEVICE_FILE_CREATE EvtDeviceFileCreate;
EVT_WDF_FILE_CLOSE EvtFileClose;
 
EVT_WDF_IO_QUEUE_IO_DEFAULT EvtIoPDPControlDevice;
 
 
 
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject , IN PUNICODE_STRING RegistryPath )
{  
    NTSTATUS status;    
    WDF_OBJECT_ATTRIBUTES object_attribs;
 
    //驅動對象相關
    WDF_DRIVER_CONFIG cfg;//驅動的配置
    WDFDRIVER drv = NULL;//wdf framework 驅動對象
 
    //設備對象相關
    PWDFDEVICE_INIT device_init = NULL;
    UNICODE_STRING ustring;
    WDF_FILEOBJECT_CONFIG f_cfg;
    WDFDEVICE control_device;
    PDEVICE_OBJECT dev = NULL;
    DEVICE_CONTEXT* dev_ctx = NULL;
 
    //IO QUEUE相關
    WDF_IO_QUEUE_CONFIG qcfg;
 
    KdPrint(("DriverEntry [start]\n"));
    
 
    //初始化WDF_DRIVER_CONFIG
    WDF_DRIVER_CONFIG_INIT(
                               &cfg,
                               NULL //不提供AddDevice函數
                            );
 
    cfg.DriverInitFlags = WdfDriverInitNonPnpDriver;  //指定非pnp驅動
    cfg.DriverPoolTag   = (ULONG)'PEPU';  
    cfg.EvtDriverUnload = EvtDriverUnload;  //指定卸載函數
 
    //創建一個framework驅動對象,在WDF程序裏面,WdfDriverCreate是必須要調用的。
    //framework驅動對象是其他所有wdf對象的父對象,換句話說framework驅動對象是wdf對象樹的頂點,它沒有父對象了。
    status = WdfDriverCreate(DriverObject,RegistryPath,WDF_NO_OBJECT_ATTRIBUTES,&cfg,&drv);
    if(!NT_SUCCESS(status))
    {
        goto DriverEntry_Complete;
    }
 
    KdPrint(("Create wdf driver object successfully\n"));
 
 
    //創建一個設備
 
    //先要分配一塊內存WDFDEVICE_INIT,這塊內存在創建設備的時候會用到。
    device_init = WdfControlDeviceInitAllocate(drv,&SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RW_RES_R);  
    if( device_init == NULL )  
    {    
        status = STATUS_INSUFFICIENT_RESOURCES;    
        goto DriverEntry_Complete;  
    }
 
    //創建設備的名字,內核模式下,名字類似: L"\\Device\\MyWDF_Device"
    RtlInitUnicodeString(&ustring, MYWDF_KDEVICE);  
 
    //將設備名字存入device_init中
    status = WdfDeviceInitAssignName(device_init,&ustring);
 
    if(!NT_SUCCESS(status))
    {
        goto DriverEntry_Complete;
    }
 
    KdPrint(("Device name Unicode string: %wZ (this name can only be used by other kernel mode code, like other drivers)\n", &ustring));
 
    //配置FILEOBJECT配置文件,設置FILECREATE,FILECLOSE回調。
    WDF_FILEOBJECT_CONFIG_INIT(&f_cfg,EvtDeviceFileCreate,EvtFileClose,NULL);
 
    //將FILEOBJECT的設置存入device_init中
    WdfDeviceInitSetFileObjectConfig(device_init,&f_cfg,WDF_NO_OBJECT_ATTRIBUTES);
 
    //初始化設備屬性
//    WDF_OBJECT_ATTRIBUTES_INIT(&object_attribs);
 
    //在設備屬性裏面增加一個DEVICE_CONTEXT,跟WDF_OBJECT_ATTRIBUTES_INIT相比,WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE
    //會分配一塊內存並且存入WDF_OBJECT_ATTRIBUTES裏面 (object_attribs.ContextTypeInfo)。DEVICE_CONEXT是個自定義結構。
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&object_attribs, DEVICE_CONTEXT);
 
    
    //根據前面創建的device_init來創建一個設備. (control device)
    status = WdfDeviceCreate(&device_init,&object_attribs,&control_device);
    if(!NT_SUCCESS(status))
    {
        KdPrint(("create device failed\n"));
        goto DriverEntry_Complete;
    }
 
    //得到設備的上下文
    dev_ctx = WdfObjectGetTypedContext(control_device, DEVICE_CONTEXT);
    RtlZeroMemory(dev_ctx, sizeof(DEVICE_CONTEXT));
    
    //創建IO queue
 
    //初始化IO QUEUE CONFIG, WdfIoQueueDispatchParallel意思是
    //The framework presents requests to the driver's request handlers as soon as the requests are available. 
    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&qcfg, WdfIoQueueDispatchParallel);    
    qcfg.PowerManaged = WdfFalse;  //非pnp驅動,無需電源管理。
    qcfg.EvtIoDefault = EvtIoPDPControlDevice; 
 
    //給設備control_device創建IO QUEUE
    status = WdfIoQueueCreate(control_device,&qcfg,WDF_NO_OBJECT_ATTRIBUTES,&dev_ctx->_DefaultQueue);  
    if( !NT_SUCCESS(status) )  
    {     
        KdPrint(("Create IoQueue failed, %x\n", status));
        goto DriverEntry_Complete;    
    }
        
 
    //創建符號連接,這樣用戶模式下的程序可以使用這個驅動。這個是必須的,不然用戶模式下的程序不能訪問這個設備。
    RtlInitUnicodeString(&ustring,MYWDF_LINKNAME);    
    status = WdfDeviceCreateSymbolicLink(control_device,&ustring);    
    if( !NT_SUCCESS(status) )  
    {    
        KdPrint(("Failed to create Link\n"));     
        goto DriverEntry_Complete;  
    }    
 
    KdPrint(("Create symbolic link successfully, %wZ (user mode code should use this name, like in CreateFile())\n", &ustring));
 
    WdfControlFinishInitializing(control_device);//創建設備完成。
 
    /*******************************************
    到這裏,我們就成功創建了一個control device。
    control device 是不支持png和power的,而且我們也不需要手工是刪除。
    因爲framework會幫我們刪除,看MSDN
    
    If your driver creates control device objects but does not create framework device objects that support PnP and power management, 
    the driver does not have to delete the control device objects. 
    
    In this case, the framework deletes the control device objects after the driver's EvtDriverUnload callback function returns. 
    更多細節看MSDN,如
    http://msdn.microsoft.com/en-us/library/windows/hardware/ff545424(v=vs.85).aspx
    *******************************************/
 
 
    KdPrint(("Create device object successfully\n"));
 
    
 
 
    KdPrint(("DriverEntry succeeds [end]\n"));
    
DriverEntry_Complete:
 
    return status;
}
 
 
 
static VOID EvtDriverUnload( WDFDRIVER Driver )
{  
    KdPrint(("unload driver\n"));
    KdPrint(("Doesn't need to clean up the devices, since we only have control device here\n"));
}/* EvtDriverUnload */
 
VOID EvtDeviceFileCreate( __in WDFDEVICE Device, __in WDFREQUEST Request, __in WDFFILEOBJECT FileObject )
{
    KdPrint(("EvtDeviceFileCreate"));
 
    WdfRequestComplete(Request, STATUS_SUCCESS);
}
 
VOID EvtFileClose( __in  WDFFILEOBJECT FileObject )
{
    KdPrint(("EvtFileClose"));
}
 
static VOID EvtIoPDPControlDevice( WDFQUEUE Queue , WDFREQUEST Request )
{
    //從WDFREQUEST裏面獲取參數
    WDF_REQUEST_PARAMETERS Params;  
    WDF_REQUEST_PARAMETERS_INIT(&Params);  
    WdfRequestGetParameters(Request,&Params);
 
    KdPrint(("EvtIoPDPControlDevice, type: %x\n", Params.Type));
    
    WdfRequestComplete(Request, STATUS_SUCCESS);
 
}

簡單調用一下:

// MyTest.cpp : Defines the entry point for the console application.
//
 
#include "stdafx.h"
#include <Windows.h>
 
#define MYDEVICE L"\\\\.\\MyWDF_LINK"
 
int _tmain(int argc, _TCHAR* argv[])
{
    HANDLE hDevice = CreateFile(MYDEVICE,GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
 
    if (hDevice == INVALID_HANDLE_VALUE)
    {
        wprintf(L"Failed to open device %s, err: %x\n", MYDEVICE, GetLastError());
        
    }
    else
    {
        wprintf(L"Open device %s successfully\n", MYDEVICE);
 
        byte buf[10] = {0};
        DWORD dwRet = 0;
        BOOL b = ReadFile(hDevice, buf, 10, &dwRet, NULL);
        wprintf(L"call ReadFile, ret: %d\n", b);
 
        b = WriteFile(hDevice, buf, 10, &dwRet, NULL);
        wprintf(L"call WriteFile, ret: %d\n", b);
 
        CloseHandle(hDevice);
 
        wprintf(L"Closed handle\n");
    }
 
    return 0;
}
 

可以看到:


驅動打印出了2個IO, 一個類型是3,一個是4. 查看一下msdn,3確實是READ, 4是WRITE,正確。也就是說用戶模式代碼引起了驅動調用2次回調EvtIoPDPControlDevice。這正是我們想要的,沒有問題。

typedef enum _WDF_REQUEST_TYPE { 
  WdfRequestTypeCreate                  = 0x0,
  WdfRequestTypeCreateNamedPipe         = 0x1,
  WdfRequestTypeClose                   = 0x2,
  WdfRequestTypeRead                    = 0x3,
  WdfRequestTypeWrite                   = 0x4,
  WdfRequestTypeQueryInformation        = 0x5,
  WdfRequestTypeSetInformation          = 0x6,
  WdfRequestTypeQueryEA                 = 0x7,
  WdfRequestTypeSetEA                   = 0x8,
  WdfRequestTypeFlushBuffers            = 0x9,
  WdfRequestTypeQueryVolumeInformation  = 0xa,
  WdfRequestTypeSetVolumeInformation    = 0xb,
  WdfRequestTypeDirectoryControl        = 0xc,
  WdfRequestTypeFileSystemControl       = 0xd,
  WdfRequestTypeDeviceControl           = 0xe,
  WdfRequestTypeDeviceControlInternal   = 0xf,
  WdfRequestTypeShutdown                = 0x10,
  WdfRequestTypeLockControl             = 0x11,
  WdfRequestTypeCleanup                 = 0x12,
  WdfRequestTypeCreateMailSlot          = 0x13,
  WdfRequestTypeQuerySecurity           = 0x14,
  WdfRequestTypeSetSecurity             = 0x15,
  WdfRequestTypePower                   = 0x16,
  WdfRequestTypeSystemControl           = 0x17,
  WdfRequestTypeDeviceChange            = 0x18,
  WdfRequestTypeQueryQuota              = 0x19,
  WdfRequestTypeSetQuota                = 0x1A,
  WdfRequestTypePnp                     = 0x1B,
  WdfRequestTypeOther                   = 0x1C,
  WdfRequestTypeUsb                     = 0x40,
  WdfRequestTypeNoFormat                = 0xFF,
  WdfRequestTypeMax                     = 0x100
} WDF_REQUEST_TYPE;

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章