WDF驱动开发-使用设备接口

设备接口是指向应用程序可用于访问设备的即插即用 (PnP) 设备的符号链接。 用户模式应用程序可以将接口的符号链接名称传递给 API 元素,例如 Microsoft Win32 CreateFile 函数。 若要获取设备接口的符号链接名称,用户模式应用程序可以调用 配置管理器函数 或 SetupApi 函数。 

每个设备接口都属于一个 设备接口类。 例如,CD-ROM 设备的驱动程序堆栈可能会提供属于 GUID_DEVINTERFACE_CDROM 类的接口。 其中一个 CD-ROM 设备的驱动程序会注册 GUID_DEVINTERFACE_CDROM 类的实例,以通知系统和应用程序 CD-ROM 设备可用。 

注册设备接口

若要注册设备接口类的实例,基于框架的驱动程序可以在设备启动之前或之后调用 WdfDeviceCreateDeviceInterface 。 如果驱动程序支持接口的多个实例,则可以为每个实例分配唯一的引用字符串。

驱动程序注册设备接口后,驱动程序可以调用 WdfDeviceRetrieveDeviceInterfaceString 来获取系统已分配给设备接口的符号链接名称。

启用和禁用设备接口

在设备启动之前创建的接口 (例如,当设备通过 PnP 枚举并启动时,框架会自动启用从 EvtDriverDeviceAdd、 EvtChildListCreateDevice 或 EvtDevicePrepareHardware) 创建的接口。 若要防止在 PnP 启动期间自动启用接口,请从同一回调函数调用 WdfDeviceSetDeviceInterfaceStateEx , 在 PnP 启动之前将该接口的 EnableInterface 参数设置为 FALSE。

在设备已启动后创建的接口不会自动启用。 驱动程序必须调用 WdfDeviceSetDeviceInterfaceState 或 WdfDeviceSetDeviceInterfaceStateEx 才能启用此类接口。

当设备进行 PnP 删除时,将自动禁用所有接口。 请注意,任何设备电源状态更改或 PnP 资源重新平衡都不会更改接口的状态。

如有必要,驱动程序可以禁用和重新启用设备接口。 例如,如果驱动程序确定其设备已停止响应,则驱动程序可以调用 WdfDeviceSetDeviceInterfaceState 或 WdfDeviceSetDeviceInterfaceStateEx 来禁用设备的接口并禁止应用程序获取接口的新句柄。 (接口的现有句柄不受影响。) 如果设备稍后可用,驱动程序可以再次调用 WdfDeviceSetDeviceInterfaceState 或 WdfDeviceSetDeviceInterfaceStateEx 以重新启用接口。

接收访问设备接口的请求

当应用程序或内核模式组件请求访问驱动程序的设备接口时,框架会调用驱动程序的 EvtDeviceFileCreate 回调函数。 驱动程序可以调用 WdfFileObjectGetFileName ,以获取应用程序或内核模式组件正在访问的设备或文件的名称。 如果驱动程序在注册设备接口时指定了引用字符串,则操作系统将在 WdfFileObjectGetFileName 返回的文件或设备名称中包含引用字符串。

访问其他驱动程序的设备接口

Kernel-Mode驱动程序框架 (KMDF) 驱动程序或User-Mode(UMDF版本 2) 驱动程序框架可以在驱动程序中注册以通知其他驱动程序提供的设备接口的到达或删除,然后创建 远程 I/O 目标 以与设备接口表示的设备通信。
为了注册设备接口事件的通知,KMDF 驱动程序调用 IoRegisterPlugPlayNotification,而 UMDF 2 驱动程序调用 CM_Register_Notification。 在这两种情况下,驱动程序从其 EvtDriverDeviceAdd 回调函数调用相应的例程。

1. 下面的代码示例演示本地 UMDF 2 驱动程序如何注册通知,然后打开远程 I/O 目标:

UNICODE_STRING ref;
    RtlInitUnicodeString(&ref, MY_HID_FILTER_REFERENCE_STRING);
    status = WdfDeviceCreateDeviceInterface(
                 hDevice,
                 (LPGUID) &GUID_DEVINTERFACE_MY_HIDFILTER_DRIVER,
                 &ref // ReferenceString
             );

    if (!NT_SUCCESS (status)) {
        MyKdPrint( ("WdfDeviceCreateDeviceInterface failed 0x%x\n", status));
        return status;
    }

2. 本地驱动程序从 EvtDriverDeviceAdd 调用 CM_Register_Notification,以便在设备接口可用时注册通知。 提供指向当设备接口可用时框架调用的通知回调例程的指针。

DWORD cmRet;
    CM_NOTIFY_FILTER cmFilter;

    ZeroMemory(&cmFilter, sizeof(cmFilter));
    cmFilter.cbSize = sizeof(cmFilter);
    cmFilter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE;
    cmFilter.u.DeviceInterface.ClassGuid = GUID_DEVINTERFACE_MY_HIDFILTER_DRIVER;

    cmRet = CM_Register_Notification(
                &cmFilter,                     // PCM_NOTIFY_FILTER pFilter,
                (PVOID) hDevice,               // PVOID pContext,
                MyCmInterfaceNotification,    // PCM_NOTIFY_CALLBACK pCallback,
                &fdoData->CmNotificationHandle // PHCMNOTIFICATION pNotifyContext
                );
    if (cmRet != CR_SUCCESS) {
        MyKdPrint( ("CM_Register_Notification failed, error %d\n", cmRet));
        status = STATUS_UNSUCCESSFUL;
        return status;
    }

3. 每次指定的设备接口到达或删除时,系统都会调用本地驱动程序的通知回调例程。 回调例程可以检查 EventData 参数以确定到达的设备接口。 然后,它可能会将工作项排队以打开设备接口:

DWORD 
MyCmInterfaceNotification(
    _In_ HCMNOTIFICATION       hNotify,
    _In_opt_ PVOID             Context,
    _In_ CM_NOTIFY_ACTION      Action,
    _In_reads_bytes_(EventDataSize) PCM_NOTIFY_EVENT_DATA EventData,
    _In_ DWORD                 EventDataSize
    )
{
    PFDO_DATA fdoData;
    UNICODE_STRING name;
    WDFDEVICE device;
    NTSTATUS status;
    WDFWORKITEM workitem;

    UNREFERENCED_PARAMETER(hNotify);
    UNREFERENCED_PARAMETER(EventDataSize);

    device = (WDFDEVICE) Context;
    fdoData = ToasterFdoGetData(device);

    switch(Action) {
    case CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL: 
        MyKdPrint( ("MyCmInterfaceNotification: Arrival of %S\n",
            EventData->u.DeviceInterface.SymbolicLink));

        //
        // Enqueue a work item to open target
        //

        break;
    case CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL: 
        MyKdPrint( ("MyCmInterfaceNotification: removal of %S\n",
            EventData->u.DeviceInterface.SymbolicLink));
        break;
    default:
        MyKdPrint( ("MyCmInterfaceNotification: Arrival unknown action\n"));
        break;
    }

    return 0;
}

4. 从工作项回调函数中,本地驱动程序调用 WdfIoTargetCreate 来创建远程目标,并调用 WdfIoTargetOpen 打开远程 I/O 目标。

调用 WdfIoTargetOpen 时,驱动程序可以选择注册 EvtIoTargetQueryRemove 回调函数来接收删除通知以及拒绝删除的机会。 如果驱动程序不提供 EvtIoTargetQueryRemove,框架在删除设备时关闭 I/O 目标。

在极少数情况下,UMDF 2 驱动程序可以再次调用 CM_Register_Notification ,以注册设备删除通知。 例如,如果驱动程序调用 CreateFile 来获取设备接口的 HANDLE,它应注册设备删除通知,以便可以正确响应查询删除尝试。 在大多数情况下,UMDF 2 驱动程序只 调用CM_Register_Notification 一次,并且依赖于 WDF 支持来删除设备:

VOID 
EvtWorkItem(
    _In_ WDFWORKITEM WorkItem
)
{
    // 
    // create and open remote target
    //

    return;
}

  • 23
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值