WDF驱动学习笔记一 - 熊猫正正的日志

最近在学习驱动开发,学习心得也发出来与大家分享一下~~~

回首微软十年,驱动开发模型从VxD--->WDM--->WDF,工具从VtoolsD---->DDK---->WDK,微软总是在不断的前进着~~~~

 

操作系统也是从DOS->Win95--->Win98--->Win2000--->WinNT--->Winxp--->Win Vista--->Win7,同样微软也在不断前进着~~

 

申明全球全大的软件供应商,微软总是保持着他的霸主地位,尽管几年前,有人就说微软不行了,但微软不愧为微软,我相信在未来人工智能的时代,微软将继续引领时代,到那时我相信每个机器人的芯片里面都会有一个微软---微型软件操作系统的存在~~

 

好了,闲话不多说了,下面进入正题~~~~

在微软最新的操作系统中,就隔入了大量的WDF模型的驱动,就好像经典的window 2000一样,window xp也会慢慢的淡出人们的视线~~

 

WDF驱动程序包括两个类型:一个是内核级的,称为KMDF(Kernel-Model Driver),为SYS文件;另一个是用户级的,称为UMDF(User-Model Driver Framework),为DLL文件。

内核模式驱动程序:这类驱动程序作为内核模式操作系统组件的一部分执行,它们管理I/O、即插即用、内存、进程与线程、安全等。内核模式驱动程序通常为分层结构

用户模式驱动程序:这类驱动程序通常提供Win32应用程序与内核模式驱动程序或其他操作系统组件之间的接口。用户模式驱动程序支持基于协议或基于串行总线(如摄像机和便携音乐播放器)的设备~~

 

先给大家介绍KMDF吧~~

KMDF为驱动程序提供了基于对象的接口,包括:

    方法(Object methods):可以用来对对象执行一个操作,或者取得/设置对象的属性。

    事件回调函数:由驱动程序提供。每个回调函数都与可能在对象上发生的一个事件相关联。当事件发生时,KMDF调用相关联的回调函数。

    属性:存储在对象内的一些可以获取/设置的值。

    句柄(Object handles):基于KMDF的驱动程序从不直接访问KMDF的对象,而是传递对象句柄,通过对象的方法来访问。

KMDF定义了一些可供驱动程序使用的对象类型:

    KMDF驱程对象:代表各个驱动程序;
    KMDF设备对象:代表驱动程序支持的各个设备;
    KMDF队列对象:代表一个设备的I/O请求队列;
    KMDF请求对象:代表I/O请求队列接收的每个I/O请求。

 

一个基于KMDF的驱动程序包括:

一个DriverEntry例程,在驱动程序加载时调用。
一套事件回调函数。例如,KMDF设备对象定义了一个EvtDeviceD0Entry回调函数,如果驱动程序实现了这个函 数,KMDF将调用EvtDeviceD0Entry函数,每当设备进入工作状态(D0)。

KMDF是对Windows驱动程序模型(WDM)接口的一个包装。虽然KMDF简化了WDM中的许多概念,并且彻底隐藏了一些其他的概念,使你不必再同它们打交道了。有一些基本的WDM概念还是应该理解的,具体如下:

    驱动程序分类:基于Windows的驱动程序分为3类:总线驱动功能驱动过滤器驱动

 总线驱动通过总线枚举支持I/O总线。总线枚举就是检测已经接入到总线上的子设备并且报告它们的特性。
 功能驱动控制总线和设备的I/O操作。
 
 过滤器驱动接收、复查并且可能修改在应用程序和驱动程序之间的数据流,或者两个驱动程序之间的数据流。
 
 总线驱动本质上说就是能够枚举子设备的功能驱动。反过来说,当同一个驱动程序在处理访问总线适配器硬件的I/O操作时,就扮演了功能驱动的角色。

    驱动程序堆栈:在Windows操作系统中,各个WDM驱动程序被垂直组织成层状结构,称为驱动程序堆栈。堆栈最上层的驱动程序一般接收来自应用程序的I/O请求(当请求通过I/O管理器后),而底层的驱动程序一般与负责硬件通讯。

一个简单的驱动程序堆栈包括,一个在最下面的总线驱动,处理总线相关的I/O操作并进行子设备枚举。再加上几个具体设备的功能驱动,然后还有一些在功能驱动上面或者下面的过滤器驱动。

    设备堆栈:略

    I/O请求包(IRP):基于KMDF的驱动程序一般不直接访问IRP。KMDF把代表读、写和IoCtl操作的IRP转换为KMDF请求对象,并接收到I/O队列中。KMDF在内部处理即插即用和电源管理IRP,然后利用事件回调函数来通知驱动程序。

 

基本原理就这些,下面给个非常简单的实例吧~~~

public.h

#ifndef _USER_H
#define _USER_H

#include <initguid.h>

DEFINE_GUID(CharSample_DEVINTERFACE_GUID, \
   0xbd083159, 0xeb56, 0x437e, 0xbb, 0x98, 0x17, 0x65, 0xe4, 0x40, 0x81, 0xe);

#define CharSample_IOCTL_800 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)

#endif

private.h

#pragma warning(disable:4200)  //
#pragma warning(disable:4201)  // nameless struct/union
#pragma warning(disable:4214)  // bit field types other than int

#include <ntddk.h>
#include <wdf.h>

#include "public.h"

#ifndef _H
#define _H

NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT  DriverObject,
    IN PUNICODE_STRING RegistryPath
    );

NTSTATUS
CharSample_EvtDeviceAdd(
    IN WDFDRIVER       Driver,
    IN PWDFDEVICE_INIT DeviceInit
    );

VOID
CharSample_EvtIoDeviceControl(
    IN WDFQUEUE   Queue,
    IN WDFREQUEST Request,
    IN size_t     OutputBufferLength,
    IN size_t     InputBufferLength,
    IN ULONG      IoControlCode
    );

#endif

resource.h

#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC                     1
#define _APS_NEXT_RESOURCE_VALUE        101
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1000
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

 

driver.c


#include "private.h"

#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DriverEntry)
#endif

NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT  DriverObject,
    IN PUNICODE_STRING RegistryPath
    )
/*++

Routine Description:
    DriverEntry initializes the driver and is the first routine called by the
    system after the driver is loaded.

Parameters Description:

    DriverObject - represents the instance of the function driver that is loaded
    into memory. DriverEntry must initialize members of DriverObject before it
    returns to the caller. DriverObject is allocated by the system before the
    driver is loaded, and it is released by the system after the system unloads
    the function driver from memory.

    RegistryPath - represents the driver specific path in the Registry.
    The function driver can use the path to store driver related data between
    reboots. The path does not store hardware instance specific data.

Return Value:

    STATUS_SUCCESS if successful,
    STATUS_UNSUCCESSFUL otherwise.

--*/
{
    WDF_DRIVER_CONFIG  config;
    NTSTATUS     status;

    //
    // Initiialize driver config to control the attributes that
    // are global to the driver. Note that framework by default
    // provides a driver unload routine. If you create any resources
    // in the DriverEntry and want to be cleaned in driver unload,
    // you can override that by manually setting the EvtDriverUnload in the
    // config structure. In general xxx_CONFIG_INIT macros are provided to
    // initialize most commonly used members.
    //

    WDF_DRIVER_CONFIG_INIT(&config, CharSample_EvtDeviceAdd);

    //
    // Create a framework driver object to represent our driver.
    //
    status = WdfDriverCreate(
        DriverObject,
        RegistryPath,
        WDF_NO_OBJECT_ATTRIBUTES, // Driver Attributes
        &config,     // Driver Config Info
        WDF_NO_HANDLE    // hDriver
        );

    return status;
}

 

device.c


#include "private.h"

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, CharSample_EvtDeviceAdd)
#endif

NTSTATUS
CharSample_EvtDeviceAdd(
    IN WDFDRIVER       Driver,
    IN PWDFDEVICE_INIT DeviceInit
    )
{
    NTSTATUS   status;
    WDFDEVICE   device;
    WDF_IO_QUEUE_CONFIG ioQueueConfig;

 //例程的首句PAGED_CODE,表示该例程的代码占用分页内存。
 //只能在PASSIVE_LEVEL中断级别调用该例程,否则会蓝屏。
 //如不说明,则占用系统的非分页内存,要珍惜使用。
    PAGED_CODE();

 //创建设备,没有对象属性和设备对象环境变量结构
    status = WdfDeviceCreate(&DeviceInit, WDF_NO_OBJECT_ATTRIBUTES, &device);
    if (!NT_SUCCESS(status)) {
        return status;
    }

 //初始化缺省队列配置,设置I/O请求分发处理方式为串行。
 //对这个实例而言,选择串行或并行都可以,但不能选手工。
    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig, WdfIoQueueDispatchSequential);

 //设置EvtIoDeviceControl例程,处理应用程序的DeviceIoControl()函数调用
    ioQueueConfig.EvtIoDeviceControl  = CharSample_EvtIoDeviceControl;

 //创建队列
    status = WdfIoQueueCreate(device, &ioQueueConfig, WDF_NO_OBJECT_ATTRIBUTES, NULL);
    if (!NT_SUCCESS(status)) {
        return status;
    }

 //创建设备GUID接口
    status = WdfDeviceCreateDeviceInterface(device, (LPGUID) &CharSample_DEVINTERFACE_GUID, NULL);
    if (!NT_SUCCESS(status)) {
    }

    return status;
}


queue.c


#include "private.h"

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, CharSample_EvtIoDeviceControl)
#endif

VOID
CharSample_EvtIoDeviceControl(
    IN WDFQUEUE   Queue,
    IN WDFREQUEST Request,
    IN size_t     OutputBufferLength,
    IN size_t     InputBufferLength,
    IN ULONG      IoControlCode
    )
{
    NTSTATUS  status;
    PVOID   buffer;
 CHAR   n,c[]="零一二三四五六七八九";

    PAGED_CODE();

    switch(IoControlCode) {

    case CharSample_IOCTL_800:
  if (InputBufferLength  == 0 || OutputBufferLength < 2)
  { //检查输入、输出参数有效性
   WdfRequestComplete(Request, STATUS_INVALID_PARAMETER);
  }
  else
  {
   //输入缓冲区地址可通过调用WdfRequestRetrieveInputBuffer函数获得
   //输出缓冲区地址可通过调用WdfRequestRetrieveOutputBuffer函数获得

   //获取输入缓冲区地址buffer
   //要求1字节空间
   status = WdfRequestRetrieveInputBuffer(Request, 1, &buffer, NULL);
   if (!NT_SUCCESS(status)) {
    WdfRequestComplete(Request, STATUS_UNSUCCESSFUL);
          break;
   }

   //这里buffer表示输入缓冲区地址
   //输入n=应用程序传给驱动程序的数字ASCII码
   n = *(CHAR *)buffer;
   if ((n>='0') && (n<='9'))
   { //若为数字,则处理
    n-='0'; //n=数字(0-9)

    //获取输出缓冲区地址buffer
    status = WdfRequestRetrieveOutputBuffer(Request, 2, &buffer, NULL);
    if (!NT_SUCCESS(status)) {
     WdfRequestComplete(Request, STATUS_UNSUCCESSFUL);
     break;
    }

    //这里buffer表示输出缓冲区地址
    //输出:从中文数组c[]中取出对应的数字的中文码,拷贝到输出缓冲区
    strncpy((PCHAR)buffer,&c[n*2],2);

    //完成I/O请求,驱动程序传给应用程序的数据长度为2字节(一个中文)
    WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, 2);
   }
   else //否则返回无效参数
    WdfRequestComplete(Request, STATUS_INVALID_PARAMETER);
  }
        break;

    default :
        status = STATUS_INVALID_DEVICE_REQUEST;
  WdfRequestCompleteWithInformation(Request, status, 0);
        break;
    }

    return;
}

 

sources

TARGETNAME=CharSample
TARGETTYPE=DRIVER

KMDF_VERSION=1
INF_NAME=CharSample

MISCFILES=$(OBJ_PATH)\$(O)\$(INF_NAME).inf
NTTARGETFILES=

C_DEFINES= $(C_DEFINES)

INCLUDES=$(INCLUDES);..\..\inc           

SOURCES=driver.c \
        device.c \
        queue.c  \
        WDFSample.rc

makefile.inc

_LNG=$(LANGUAGE)
_INX=.
STAMP=stampinf -f $@ -a $(_BUILDARCH)

$(OBJ_PATH)\$(O)\$(INF_NAME).inf: $(_INX)\$(INF_NAME).inx 
    copy $(_INX)\$(@B).inx $@
    $(STAMP)

 

CharSample.inx

;Module Name:
;    CharSample.INF
;
;Abstract:
;    INF file for installing the Windows Driver Frameworks CharSample Driver
;
;Installation Notes: 
;    Using Devcon: Type "devcon install CharSample.inf root\CharSample" to install
;
;--*/

[Version]
Signature="$WINDOWS NT$"
Class=WDFBOOK
ClassGuid={EF1941A7-645B-4668-B05B-287D30169435}
Provider=%ProviderName%
DriverVer=12/28/2008,6.0.6000.16386

; ================= Class section =====================

[ClassInstall32]
Addreg=SampleClassReg    

[SampleClassReg]
HKR,,,0,�viceClassName%
HKR,,Icon,,-18

;*****************************************
; CharSample  Install Section
;*****************************************

[Manufacturer]
%MfgName%=Standard,NTx86

; Following section is meant for Windows 2000 as it 
; cannot parse decorated model sections
[Standard]
;
; Hw Id is root\CharSample
;
%CharSample.DeviceDesc%=CharSample_Device, root\CharSample

; Decorated model section take precedence over undecorated 
; ones on XP and later.
[Standard.NTx86]
%CharSample.DeviceDesc%=CharSample_Device, root\CharSample

[DestinationDirs]
CharSample_Files_Driver = 12

[CharSample_Device.NT]
CopyFiles=CharSample_Files_Driver

[CharSample_Files_Driver]
CharSample.sys

;-------------- Service installation
[CharSample_Device.NT.Services]
AddService = CharSample,0x00000002, CharSample_AddService

; -------------- CharSample driver install sections
[CharSample_AddService]
DisplayName    = %CharSample.SVCDESC%
ServiceType    = 1               ; SERVICE_KERNEL_DRIVER
StartType      = 3               ; SERVICE_DEMAND_START 
ErrorControl   = 1               ; SERVICE_ERROR_NORMAL
ServiceBinary  = %\CharSample.sys

;
;--- WDF Coinstaller installation ----
;

[DestinationDirs]
CoInstaller_CopyFiles = 11

[CharSample_Device.NT.CoInstallers]
CopyFiles=CoInstaller_CopyFiles
AddReg=CoInstaller_AddReg

[CoInstaller_CopyFiles]
wdfcoinstaller01007.dll

[CoInstaller_AddReg]
HKR,,CoInstallers32,0x00010000, "wdfcoinstaller01007.dll,WdfCoInstaller"

[CharSample_Device.NT.Wdf]
KmdfService =  CharSample, CharSample_wdfsect

[CharSample_wdfsect]
KmdfLibraryVersion = 1.7


[Strings]
ProviderName="Windows最新WDF设备驱动程序开发"
MfgName="武安河"
DeviceClassName="WDF范例"
CharSample.DeviceDesc = "CharSample"
CharSample.SVCDESC = "WDF CharSample Service"

 

驱动的代码就这些了,编译方法和编译WDM一样的,我就不说了~~~~

注意编译好之后安装的时候要有WdfCoInstaller01007.dll文件的支持~~用EZDriverInstaller安装超简单

 

应用层代码比较简单,主要是通过DeviceIoControl与驱动层通信~~~代码如下:

// Test_CharSample.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <windows.h>
#include <setupapi.h>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <winioctl.h>

#include "public.h"

PCHAR
GetDevicePath(
    IN  LPGUID InterfaceGuid
    );

int main(int argc, char* argv[])
{
 PCHAR  DevicePath;
    HANDLE hDevice = INVALID_HANDLE_VALUE;

 printf("Application Test_CharSample starting...\n");

    DevicePath = GetDevicePath((LPGUID)&CharSample_DEVINTERFACE_GUID);

    hDevice = CreateFile(DevicePath,
                         GENERIC_READ|GENERIC_WRITE,
                         FILE_SHARE_READ | FILE_SHARE_WRITE,
                         NULL,
                         OPEN_EXISTING,
                         0,
                         NULL );

    if (hDevice == INVALID_HANDLE_VALUE) {
  printf("ERROR opening device: (%0x) returned from CreateFile\n", GetLastError());
        return 0;
    }

 printf("OK.\n");

 CHAR bufInput[1]; // Input to device
 CHAR bufOutput[2]; // Output from device
 ULONG nOutput; // Count written to bufOutput

 printf("请输入数字(0-9)\n"); 
l0: bufInput[0] = _getch();
 if ((bufInput[0]<'0') || (bufInput[0]>'9')) goto l0;
 _putch(bufInput[0]);
   
 // Call device IO Control interface (CharSample_IOCTL_800) in driver
 if (!DeviceIoControl(hDevice,
       CharSample_IOCTL_800,
       bufInput,
       1,
       bufOutput,
       2,
       &nOutput,
       NULL)
    )
 {
  printf("ERROR: DeviceIoControl returns %0x.", GetLastError());
        goto exit;
 }
 printf(":"); 
 _putch(bufOutput[0]); 
 _putch(bufOutput[1]); 
 printf("\n");
 
exit:

    if (hDevice != INVALID_HANDLE_VALUE) {
        CloseHandle(hDevice);
    }
 return 0;
}

PCHAR
GetDevicePath(
    IN  LPGUID InterfaceGuid
    )
{
    HDEVINFO HardwareDeviceInfo;
    SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
    PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData = NULL;
    ULONG Length, RequiredLength = 0;
    BOOL bResult;

    HardwareDeviceInfo = SetupDiGetClassDevs(
                             InterfaceGuid,
                             NULL,
                             NULL,
                             (DIGCF_PRESENT | DIGCF_DEVICEINTERFACE));

    if (HardwareDeviceInfo == INVALID_HANDLE_VALUE) {
        printf("SetupDiGetClassDevs failed!\n");
        exit(1);
    }

    DeviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);

    bResult = SetupDiEnumDeviceInterfaces(HardwareDeviceInfo,
                                              0,
                                              InterfaceGuid,
                                              0,
                                              &DeviceInterfaceData);

    if (bResult == FALSE) {
/*
        LPVOID lpMsgBuf;

        if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
                          FORMAT_MESSAGE_FROM_SYSTEM |
                          FORMAT_MESSAGE_IGNORE_INSERTS,
                          NULL,
                          GetLastError(),
                          MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                          (LPSTR) &lpMsgBuf,
                          0,
                          NULL
                          )) {

            printf("Error: %s", (LPSTR)lpMsgBuf);
            LocalFree(lpMsgBuf);
        }
*/
        printf("SetupDiEnumDeviceInterfaces failed.\n");

        SetupDiDestroyDeviceInfoList(HardwareDeviceInfo);
        exit(1);
    }

    SetupDiGetDeviceInterfaceDetail(
        HardwareDeviceInfo,
        &DeviceInterfaceData,
        NULL,
        0,
        &RequiredLength,
        NULL
        );

    DeviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA) LocalAlloc(LMEM_FIXED, RequiredLength);

    if (DeviceInterfaceDetailData == NULL) {
        SetupDiDestroyDeviceInfoList(HardwareDeviceInfo);
        printf("Failed to allocate memory.\n");
        exit(1);
    }

    DeviceInterfaceDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

    Length = RequiredLength;

    bResult = SetupDiGetDeviceInterfaceDetail(
                  HardwareDeviceInfo,
                  &DeviceInterfaceData,
                  DeviceInterfaceDetailData,
                  Length,
                  &RequiredLength,
                  NULL);

    if (bResult == FALSE) {
/*
        LPVOID lpMsgBuf;

        if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
                      FORMAT_MESSAGE_FROM_SYSTEM |
                      FORMAT_MESSAGE_IGNORE_INSERTS,
                      NULL,
                      GetLastError(),
                      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                      (LPSTR) &lpMsgBuf,
                      0,
                      NULL
                      )) {

            MessageBox(NULL, (LPCTSTR) lpMsgBuf, "Error", MB_OK);
            LocalFree(lpMsgBuf);
        }
*/
        printf("Error in SetupDiGetDeviceInterfaceDetail\n");

        SetupDiDestroyDeviceInfoList(HardwareDeviceInfo);
        LocalFree(DeviceInterfaceDetailData);
        exit(1);
    }

    return DeviceInterfaceDetailData->DevicePath;

}

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值