Windows驱动开发(三)一个WDF入门实例

本文详细介绍了Windows驱动开发中的关键函数DriverEntry和DeviceAdd的用途及其实现过程。DriverEntry作为驱动的入口点,负责初始化和注册回调函数。DeviceAdd则用于创建设备对象,设置PnP和电源管理回调,并配置IO队列。文章还讨论了硬件资源分配的过程,并提到了WDF驱动与NT驱动的区别。此外,文章提到了驱动程序接口的创建,以便与其他程序通信。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上一章大概介绍了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例程需要完成以下这些功能:

  1. 注册WDF的DeviceAdd回调函数,这是WDF驱动与NT驱动最大的不同之一
  2. 创建一个驱动对象,用于向框架注册我们的驱动程序

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

}

此例程主要做了下面几件重要的事情:

  1. 创建初始化设备对象及其上下文
  2. 若该WDF驱动为支持PnP的,则要注册PnP相关的一系列回调函数
  3. 若该驱动需要处理IO,则需要配置一个消息队列,注册处理例程以及制定IO请求类型等等
  4. 若该驱动需要与其他程序进行交互,则可以注册一个GUID接口用于通信,当然,也可以使用符号链接的形式。

在PnP的管理例程注册部分,可以看到几个典型的回调,即PrepareHardware,ReleaseHardware,DeviceEntry以及DeviceExit。

PrepareHardware以及ReleaseHardware关系着你的硬件是否可以获得Windows分配的资源,而在PrepareHardware里面,你需要去实现内存资源,内存地址映射,IO端口映射以及CPU中断资源的分配等等。

在WDF中,给硬件分配资源的大致流程如下:

  1. 当插入一个PnP设备时,总线枚举驱动识别该设备
  2. 调用总线驱动的DeviceResourceQuery,创建资源列表
  3. 调用总线驱动的DeviceResourceRequirementQuery,创建资源需求列表
  4. 由PnP管理器来匹配驱动,随后申请必要的资源,将信息发送给驱动程序
  5. 当你的设备最终进入工作预备状态时,前面写的PrepareHardware即生效
  6. 之后进入到DeviceEntry例程,通常,DeviceEntry例程并需要额外处理事务

WDF的很多Sample之前在微软的msdn sample里面都有,包含了各种各样的硬件设备驱动,过滤驱动,枚举驱动等等,可惜不知道为什么被微软下架了。。。不过好在万能的百度上还是能找得到的,不过这里面的sample都没有去实现具体的功能,只是阐述了一个个驱动开发的模型,虽然代码量很小,但这已经是我们能看到的最好的例子了,熟悉了之后,自己去具体开发符合你需求的功能驱动就不那么困难。

后面的话,因为这几年做的很多实际的驱动,会不定期写一些笔记,当然,不在仅限于小端口驱动了~

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值