上一章大概介绍了WDM/WDF的IOControl,
链接:https://blog.csdn.net/o0xwh_93150o/article/details/104234021
鸽太久了,刚好闲下来,就把这个实例入门篇给补了吧。话不多说,先上代码。
DriverEntry例程:
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath) {
WDF_DRIVER_CONFIG config;
NTSTATUS status = STATUS_SUCCESS;
WDF_OBJECT_ATTRIBUTES attributes;
//
// Initialize WPP Tracing
//
WPP_INIT_TRACING( DriverObject, RegistryPath );
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry");
//
// Register a cleanup callback so that we can call WPP_CLEANUP when
// the framework driver object is deleted during driver unload.
//
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
attributes.EvtCleanupCallback = Spw_PCIeEvtDriverContextCleanup;
WDF_DRIVER_CONFIG_INIT(&config, Spw_PCIeEvtDeviceAdd);
status = WdfDriverCreate(DriverObject,
RegistryPath,
&attributes,
&config,
WDF_NO_HANDLE);
if (!NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "WdfDriverCreate failed %!STATUS!", status);
WPP_CLEANUP(DriverObject);
return status;
}
TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Exit");
return status;
}
每一个WDF驱动必须有一个DriverEntry例程,当操作系统监测到有硬件设备插入时,就会找到对应的DriverEntry例程,DriverEntry例程需要完成以下这些功能:
- 注册WDF的DeviceAdd回调函数,这是WDF驱动与NT驱动最大的不同之一
- 创建一个驱动对象,用于向框架注册我们的驱动程序
在WDF中,真正被我们当作初始化的函数,变成了DeviceAdd,所以可以把相关的初始化行为全部丢在DeviceAdd中。
DeviceAdd例程:
NTSTATUS
Spw_PCIeEvtDeviceAdd(_In_ WDFDRIVER Driver,
_Inout_ PWDFDEVICE_INIT DeviceInit) {
NTSTATUS status = STATUS_SUCCESS;
WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
WDF_OBJECT_ATTRIBUTES deviceAttributes;
WDFDEVICE device;
PDEVICE_CONTEXT deviceContext;
WDFQUEUE queue;
WDF_IO_QUEUE_CONFIG queueConfig;
UNREFERENCED_PARAMETER(Driver);
PAGED_CODE();
WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect);
//When the I/O manager sends a request for buffered I/O, the IRP contains an internal copy of the caller's buffer
//rather than the caller's buffer itself. The I/O manager copies data from the caller's buffer to the internal buffer
//during a write request or from the internal buffer to the caller's buffer when the driver completes a read
//request.
//The WDF driver receives a WDF request object, which in turn contains an embedded WDF memory object.
//The memory object contains the address of the buffer on which the driver should operate.
//初始化即插即用和电源管理例程配置结构
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
//设置即插即用基本例程
pnpPowerCallbacks.EvtDevicePrepareHardware = Spw_PCIeEvtDevicePrepareHardware;
pnpPowerCallbacks.EvtDeviceReleaseHardware = Spw_PCIeEvtDeviceReleaseHardware;
pnpPowerCallbacks.EvtDeviceD0Entry = Spw_PCIeEvtDeviceD0Entry;
pnpPowerCallbacks.EvtDeviceD0Exit = Spw_PCIeEvtDeviceD0Exit;
//注册即插即用和电源管理例程
WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT);
//deviceAttributes.EvtCleanupCallback = Spw_PCIeEvtDriverContextCleanup;
//
// Set WDFDEVICE synchronization scope. By opting for device level
// synchronization scope, all the queue and timer callbacks are
// synchronized with the device-level spinlock.
//
deviceAttributes.SynchronizationScope = WdfSynchronizationScopeDevice;
status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
if (!NT_SUCCESS(status)) {
return status;
}
deviceContext = GetDeviceContext(device);
//用default初始化default 队列,用另一个初始化非default队列
WDF_IO_QUEUE_CONFIG_INIT(&queueConfig,
WdfIoQueueDispatchSequential);
queueConfig.EvtIoDeviceControl = Spw_PCIeEvtIoDeviceControl;
status = WdfIoQueueCreate(device, &queueConfig, WDF_NO_OBJECT_ATTRIBUTES, &queue);
if (!NT_SUCCESS(status)) {
return status;
}
//非默认队列,必须指定要分发的I/O请求类型
//The WdfDeviceConfigureRequestDispatching method causes the framework to queue a specified type of I/O requests to a specified I/O queue.
status = WdfDeviceConfigureRequestDispatching(device,
queue,
WdfRequestTypeDeviceControl);
if (!NT_SUCCESS(status)) {
return status;
}
//创建驱动程序接口与应用程序通信
status = WdfDeviceCreateDeviceInterface(device,
(LPGUID)&GUID_DEVINTERFACE_Spw_PCIe,
NULL // ReferenceString);
if (!NT_SUCCESS(status)) {
return status;
}
return status;
}
此例程主要做了下面几件重要的事情:
- 创建初始化设备对象及其上下文
- 若该WDF驱动为支持PnP的,则要注册PnP相关的一系列回调函数
- 若该驱动需要处理IO,则需要配置一个消息队列,注册处理例程以及制定IO请求类型等等
- 若该驱动需要与其他程序进行交互,则可以注册一个GUID接口用于通信,当然,也可以使用符号链接的形式。
在PnP的管理例程注册部分,可以看到几个典型的回调,即PrepareHardware,ReleaseHardware,DeviceEntry以及DeviceExit。
PrepareHardware以及ReleaseHardware关系着你的硬件是否可以获得Windows分配的资源,而在PrepareHardware里面,你需要去实现内存资源,内存地址映射,IO端口映射以及CPU中断资源的分配等等。
在WDF中,给硬件分配资源的大致流程如下:
- 当插入一个PnP设备时,总线枚举驱动识别该设备
- 调用总线驱动的DeviceResourceQuery,创建资源列表
- 调用总线驱动的DeviceResourceRequirementQuery,创建资源需求列表
- 由PnP管理器来匹配驱动,随后申请必要的资源,将信息发送给驱动程序
- 当你的设备最终进入工作预备状态时,前面写的PrepareHardware即生效
- 之后进入到DeviceEntry例程,通常,DeviceEntry例程并需要额外处理事务
WDF的很多Sample之前在微软的msdn sample里面都有,包含了各种各样的硬件设备驱动,过滤驱动,枚举驱动等等,可惜不知道为什么被微软下架了。。。不过好在万能的百度上还是能找得到的,不过这里面的sample都没有去实现具体的功能,只是阐述了一个个驱动开发的模型,虽然代码量很小,但这已经是我们能看到的最好的例子了,熟悉了之后,自己去具体开发符合你需求的功能驱动就不那么困难。
后面的话,因为这几年做的很多实际的驱动,会不定期写一些笔记,当然,不在仅限于小端口驱动了~