WDM式驱动的基本结构3 - WDM驱动程序的AddDevice例程

本文详细解释了WindowsDriverModel(WDM)中AddDevice例程的作用,它在驱动程序中负责创建设备对象,设置设备扩展,并将FDO附加到PDO。作者还介绍了创建符号链接、设备标志设置等关键步骤。
摘要由CSDN通过智能技术生成

AddDevice例程是WDM驱动所独有的,在NT驱动中没有该例程。在DriverEntry中,需要设置AddDevice例程的函数地址。设置的方式是驱动对象中有个 DriverExtension 子域,DriverExtension中有个AddDevice子域,将该子域指向AddDevice例程的函数地址。

pDriverObject->DriverExtension->AddDevice = HelloWDMAddDevice;

和DriverEntry不同,AddDevice例程的名字可以任意命名,程序员可以用更有意义的名字作为这个函数的名字。在HelloWDM例子中,使用的名称就是HelloWDMAddDevice。

#pragma PAGEDCODE
NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject,
                           IN PDEVICE_OBJECT PhysicalDeviceObject)
{ 
    PAGED_CODE();
    KdPrint(("Enter HelloWDMAddDevice\n"));

    NTSTATUS status;
    PDEVICE_OBJECT fdo;
    UNICODE_STRING devName;
    
    // 初始化UNICODE字符串
    RtlInitUnicodeString(&devName,L"\\Device\\MyWDMDevice");
    
    // 创建设备对象
    status = IoCreateDevice(
        DriverObject,
        sizeof(DEVICE_EXTENSION),
        &(UNICODE_STRING)devName,
        FILE_DEVICE_UNKNOWN,
        0,
        FALSE,
        &fdo);
    // 判断是否创建成功
    if( !NT_SUCCESS(status))
        return status;
        
    // 得到扩展设备
    PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
    
    // 将FDO附加在PDO上
    pdx->fdo = fdo;
    pdx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject);
        
    UNICODE_STRING symLinkName;
    RtlInitUnicodeString(&symLinkName,L"\\DosDevices\\HelloWDM");
    
    // 用设备扩展记录设备名和符号链接
    pdx->ustrDeviceName = devName;
    pdx->ustrSymLinkName = symLinkName;
    
    // 创建符号链接
    status = IoCreateSymbolicLink(&(UNICODE_STRING)symLinkName,&(UNICODE_STRING)devName);

    // 如果创建失败,则删除设备对象
    if( !NT_SUCCESS(status))
    {
        IoDeleteSymbolicLink(&pdx->ustrSymLinkName);
        status = IoCreateSymbolicLink(&symLinkName,&devName);
        if( !NT_SUCCESS(status))
        {
            return status;
        }
    }

    // 设置设备标志
    fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
    fdo->Flags &= ~DO_DEVICE_INITIALIZING;

    KdPrint(("Leave HelloWDMAddDevice\n"));
    return STATUS_SUCCESS;
}

从上述代码中可以看出,AddDevice例程类似于NT驱动中DriverEntry创建设备对象的相关操作,但是略有不同。AddDevice函数有两个输入参数,一个是驱动对象DriverObject,另一个是设备对象PhysicalDeviceObject。驱动对象是I/O管理器创建的驱动对象。设备对象PhysicalDeviceObject就是底层驱动创建的PDO设备对象。传进该参数的目的就是将FDO附加在PDO之上。

在AddDevice中可以分为以下几个步骤:
1. 在AddDevice通过IoCreateDevice等函数,创建了设备对象,该设备对象就是FDO,即功能驱动设备对象。和NT驱动一样,可以设置驱动对象的设备名称,也可以不设置,如果不设置设备名称,I/O管理会自动以一个数字作为该设备对象的名称。
2. 创建完FDO后,需要将FDO的地址保存下来,以便以后使用。保存的位置是在设备扩展中,在驱动程序中应该尽量避免使用全局变量,而使用设备扩展。如果该电脑中存在多个同类设备,例如,插入两个型号相同的网卡,操作系统会两次两用AddDevice例程。每个AddDevice例程创建各自的FDO,分别记录在各自的设备扩展中。
3. 驱动程序将创建的FDO附加在PDO上,附加这个动作是依靠IoAttachDeviceToDeviceStack函数实现的。IoAttachDeviceToDeviceStack的声明如下:
        

PDEVICE_OBJECT IoAttachDeviceToDeviceStack(
            PDEVICE_OBJECT SourceDevice,
            PDEVICE_OBJECT TargetDevice
            );

* SourceDevice:要附加在别的设备之上的设备。将FDO附加在PDO之上时,这个填写的是FDO的地址。
* TargetDevice:被附加的设备。将FDO附加在PDO之上时,这个填写的是PDO的地址,当FDO想附加在PDO上时,有时会在PDO和FDO上附加过滤驱动。此时,FDO其实是附加在过滤设备上,而过滤设备附加在PDO上。
* 返回值:附加以后,返回附加设备的下层设备。如果中间没有过滤驱动的话,返回值就是PDO,如果中间有过滤驱动,返回的是过滤驱动。

当FDO附加到PDO上时,PDO会通过AttachedDevice子域知道它上面的设备是FDO(或者是过滤驱动)。但是FDO却不知道自己的下层是什么设备。解决的办法是,通过设备扩展记录FDO下层的设备。下面是HelloWDM的设备扩展的定义:

typedef struct _DEVICE_EXTENSION
{
    PDEVICE_OBJECT fdo;
    PDEVICE_OBJECT NextStackDevice;
    UNICODE_STRING ustrDeviceName;    // 设备名
    UNICODE_STRING ustrSymLinkName;    // 符号链接名
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

我们在自己编写驱动程序时,可以根据自己的需要定制自己的设备扩展。子域fdo是为了保存FDO的地址,以备后用。子域NextStackDevice是为了定位设备的下一层设备。

在附加操作完成,需要设定符号链接,以便用户应用程序可以访问该设备。
4. 设置fdo的flags子域。DO_BUFFERED_IO是定义设备为“缓冲内存设备”。另外~DO_DEVICE_INITIALIZING,是将Flag上的DO_DEVICE_INITIALIZING位清零。保证设备初始化完成,这一步是必须得。


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WendyWJGu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值