Windows驱动开发笔记(2)---驱动程序结构与KMDF驱动实例分析

KMDF驱动程序模型:一个即插即用的KMDF驱动程序应该包含

一个DriverEntry例程
一个EvtDriverDeviceAdd例程
一个或者多个I/O队列
一个或者多个I/O回调例程
支持即插即用和电源管理回调例程
支持的WMI回调例程
其他回调例程,如对象清除,中断例程、DMA例程等

1.DriverEntry例程

DriverEntry例程负责驱动程序的初始化,类似linux下的module_init模块。所有驱动必须包含DriverEntry例程,,驱动程序第一次加载时,调用DriverEntry例程。在DriverEntry例程中,主要完成创建驱动对象和设置EvtDriverDeviceAdd例程地址。

驱动对象结构体WDFDRIVER需要配置结构体WDF_DRIVER_CONFIG用来完成初始化,WDF_DRIVER_CONFIG结构体如下:

typedef struct WDF_DRIVER_CONFIG{
ULONG 	                                Size;
PFN_WDF_DRIVER_DEVICE_ADD				EvtDriverDeviceAdd;
PFN_WDF_DRIVER_UNLOAD                   EvtDriverUnload;
ULONG                                   DriverInitFlags;
ULONG                                   DriverPoolTag;
}
EvtDriverDeviceAdd:  设备对象添加例程
EvtDriverUnload   :  设备对象卸载例程删除DriverEntry例程分配给驱动的资源
DriverInitFlags   :  初始化标志位

驱动对象配置结构初始化函数如下

VOID
WDF_DRIVER_CONFIG_INIT(
OUT    PWDF_DRIVER_CONFIG            config,
IN     PFN_WDF_DRIVER _DEVICE_ADD    EvtDriverDeviceAdd,
);

驱动对象创建函数如下

NTSTATUS
wdfDriverCreate(
IN   PDRIVER_OBJECT   DRIVEROBJECT,
IN   PCUNICODE_STRING RegistryPath,
IN   OPTIONAL  PWDF_OBJECT_ATTRIBUTES   DriverAttributes,
IN   PWDF_DRIVER_CONFIG Driver_Config,
OUT  OPTIONAL  WDFDRIVER*  Driver
);
DriverAttributes   :对象属性

KMDF驱动的一个基本DriverEntr例程示例

NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT  DriverObject,
    IN PUNICODE_STRING RegistryPath
    )
{
    WDF_DRIVER_CONFIG      config;          // 配置信息
    NTSTATUS		       status;
    WDF_DRIVER_CONFIG_INIT(&config, EvtDeviceAdd);
    status = WdfDriverCreate( DriverObject,RegistryPath,WDF_NO_OBJECT_ATTRIBUTES,	&config,WDF_NO_HANDLE);
    // 创建驱动对象
    return status;
    }

2.DeviceAdd例程

通过DriverEntry例程,内核完成驱动对象的创建和设置,并且将AddDeivce例程地址装入,在驱动初始化完成后,内核的Pnp管理器就会调用驱动程序的DeviceAdd例程来初始化驱动程序所控制的设备。在DeviceAdd历程中,驱动创建一个设备对象作为目标I/O设备,并将设备对象依附到设备堆栈中。当系统运行时,任何一个新设备枚举时,系统都将会调用DeviceAdd程序。
在KMDF驱动中,DeviceAddd需要完成以下任务

创建设备对象
一个或者多个I/O队列和设备GUID接口
设置各种事件的回调例程

设备对象创建函数如下

NTSTATUS
WdfDeviceCreate(
IN   OUT    PWDFDEVICE_INIT*   DeviceInit,
IN   OPTIONAL   PWDF_OBJECT_ATTRIBUTES    DeviceAttributes,
OUT   WDFDEVICE*   Device
);

创建设备GUID接口

NTSTATUS
WdfDeviceCreateDeviceInterface(
IN   WDFDEIVCE   device,
IN   CONST   GUID*InterfaceClassGUID,
IN  OPTIONAL   PCUNICODE_STRING   ReferenceString
);

DeviceAdd例程示例

NTSTATUS
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;
}

3. I/O处理例程

I/O处理例程完成驱动和应用之间的通信,包括Create、Close、Cleanup、Read、Write和DeviceControl
Read、Write和DeviceControl由队列管理,可以是默认队列,也可以由自己创建。
Create例程处理应用程序的CreateFile函数调用,Close例程处理应用程序的CloseHandle调用
设置Creat、Close、Cleanup例程的结构体WDF_FILEOBJECT_CONFIG

typedef struct    WDF_FILEOBJECT_CONFIG(
ULONG         Size;
PFN_WDF_DEVICE_FILE_CREATE   EvtDeviceFileCreate;
PFN_WDF_FILE_CLOSE           EvtFileClose;
PFN_WDF_FILE_CLEANUP         EvtFileCleanup;
WDF_TRI_STATE                AutoForwordCleanupClose;
WDF_FILEOBJECT_CLASS         FileObjectClass;
);
//配置该结构体的函数为:
VOID
WDP_FILEOBJECT_CONFIG_INIT(
OUT   PWDF_FILEOBJECT_CONFIG    FileEventCallbacks,
IN   OPTIONAL   PFN_WDF_DEVICE_FILE_CREATE   EvtDeviceFileCreate,
IN   OPTIONAL   PFN_WDF_FILE_CLOSE           EvtFileClose,
IN   OPTIONAL   PFN_WDF_FILE_CLEANUP         EvtFileCleanu
);
//设置该结构体的函数:
VOID
WdfDeviceInitSetFileObjectConfig(
IN   PWDFDEVICE_INIT   DeviceInit,
IN   PWDF_FILEOBJECT_CONFIG  FileObjectConfig,
IN   OPTIONAL   PWDF_OBJECT_ATTRIBUTES   FileObjectAttributes
);

Read、Write和DeviceControl分别处理应用程序的ReadFile、WriteFile和DeviceControl函数调用

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;
}

对于大部分驱动,WDM的默认配置已经可以完成即插即用和电源管理,这些程序不需要关于即插即用和电源管理的编程处理

参考文献:Windows设备驱动程序WDM开发第二章KMDF驱动程序框架

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值