USB通用驱动源码分析
+ -

USBCCGP FDO的启动

2021-09-15 267 1

USBCCGP FDO的启动会执行主功能号为IRP_MJ_PNP,次功功能号IRP_MN_START_DEVICE的IRP。
其函数调用关系如下:

USBCCGP_Dispatch 
        FDO_Dispatch 
                FDO_HandlePnp
                    FDO_StartDevice

函数代码如下:
fdo.c

NTSTATUS
FDO_StartDevice(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp)
{
    NTSTATUS Status;
    PFDO_DEVICE_EXTENSION FDODeviceExtension;

    /* Get device extension */
    FDODeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
    ASSERT(FDODeviceExtension->Common.IsFDO);

    /* First start lower device */
    Status = USBCCGP_SyncForwardIrp(FDODeviceExtension->NextDeviceObject, Irp);

    if (!NT_SUCCESS(Status))
    {
        /* Failed to start lower device */
        DPRINT1("FDO_StartDevice lower device failed to start with %x\n", Status);
        return Status;
    }

    /* Get descriptors */
    Status = USBCCGP_GetDescriptors(DeviceObject);
    if (!NT_SUCCESS(Status))
    {
        /* Failed to start lower device */
        DPRINT1("FDO_StartDevice failed to get descriptors with %x\n", Status);
        return Status;
    }

    /* Get capabilities */
    Status = FDO_QueryCapabilities(DeviceObject,
                                   &FDODeviceExtension->Capabilities);
    if (!NT_SUCCESS(Status))
    {
        /* Failed to start lower device */
        DPRINT1("FDO_StartDevice failed to get capabilities with %x\n", Status);
        return Status;
    }

    /* Now select the configuration */
    Status = USBCCGP_SelectConfiguration(DeviceObject, FDODeviceExtension);
    if (!NT_SUCCESS(Status))
    {
        /* Failed to select interface */
        DPRINT1("FDO_StartDevice failed to get capabilities with %x\n", Status);
        return Status;
    }

    /* Query bus interface */
    USBCCGP_QueryInterface(FDODeviceExtension->NextDeviceObject,
                           &FDODeviceExtension->BusInterface);

    /* Now enumerate the functions */
    Status = USBCCGP_EnumerateFunctions(DeviceObject);
    if (!NT_SUCCESS(Status))
    {
        /* Failed to enumerate functions */
        DPRINT1("Failed to enumerate functions with %x\n", Status);
        return Status;
    }

    /* Sanity checks */
    ASSERT(FDODeviceExtension->FunctionDescriptorCount);
    ASSERT(FDODeviceExtension->FunctionDescriptor);
    DumpFunctionDescriptor(FDODeviceExtension->FunctionDescriptor,
                           FDODeviceExtension->FunctionDescriptorCount);

    /* Now create the pdo */
    Status = FDO_CreateChildPdo(DeviceObject);
    if (!NT_SUCCESS(Status))
    {
        /* Failed */
        DPRINT1("FDO_CreateChildPdo failed with %x\n", Status);
        return Status;
    }

    /* Inform pnp manager of new device objects */
    IoInvalidateDeviceRelations(FDODeviceExtension->PhysicalDeviceObject,
                                BusRelations);

    /* Done */
    DPRINT("[USBCCGP] FDO initialized successfully\n");
    return Status;
}

可以看到,FDO_StartDevice主要实现了以下功能:
1.将IRP下传给PDO,使PDO启动。
2.获取设备的描述符:设备描述符配置描述符(包括接口描述符端点描述符和其它描述符)
3.获取设备支持的特性,如电源,热插发等。
4.解析配置描述符,并获取各个子设备信息。
5.获取总线自定义的枚举设备的接口。
6.枚举子设备PDO.
7.创建子设备。
8.通知PNP管理器发备树发生变化,有新的设备产生。

IRP同步下传是能过函数USBCCGP_SyncForwardIrp实现的:

msic.c

TSTATUS
NTAPI
USBCCGP_SyncForwardIrp(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp)
{
    KEVENT Event;
    NTSTATUS Status;

    /* Initialize event */
    KeInitializeEvent(&Event, NotificationEvent, FALSE);

    /* Copy irp stack location */
    IoCopyCurrentIrpStackLocationToNext(Irp);

    /* Set completion routine */
    IoSetCompletionRoutine(Irp,
                           USBSTOR_SyncForwardIrpCompletionRoutine,
                           &Event,
                           TRUE,
                           TRUE,
                           TRUE);

    /* Call driver */
    Status = IoCallDriver(DeviceObject, Irp);

    /* Check if pending */
    if (Status == STATUS_PENDING)
    {
        /* Wait for the request to finish */
        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);

        /* Copy status code */
        Status = Irp->IoStatus.Status;
    }

    /* Done */
    return Status;
}

可能看到,创建一个事件,初始化为通知事件,默认为未激活态。
然后将当前的STACK_LOCATION拷贝到下一层,使其具有相同的STACK_LOCATION,然后设置下层完成函数为USBSTOR_SyncForwardIrpCompletionRoutine.这样当下层完成IRP时,会调用完成函数,而我们在完成函数中将事件置为激活,这样在本层驱动通过等到这个事件就可以知道下层已经完成,即设备已经启动了。

完成函数的代码如下:
msic.c

NTSTATUS
NTAPI
USBSTOR_SyncForwardIrpCompletionRoutine(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp,
    PVOID Context)
{
    if (Irp->PendingReturned)
    {
        KeSetEvent((PKEVENT)Context, IO_NO_INCREMENT, FALSE);
    }
    return STATUS_MORE_PROCESSING_REQUIRED;
}

附相关函数源代码:

IoCopyCurrentIrpStackLocationToNext

FORCEINLINE
VOID
IoCopyCurrentIrpStackLocationToNext(
    _Inout_ PIRP Irp
)
/*--

Routine Description:

    This routine is invoked to copy the IRP stack arguments and file
    pointer from the current IrpStackLocation to the next
    in an I/O Request Packet (IRP).

    If the caller wants to call IoCallDriver with a completion routine
    but does not wish to change the arguments otherwise,
    the caller first calls IoCopyCurrentIrpStackLocationToNext,
    then IoSetCompletionRoutine, then IoCallDriver.

Arguments:

    Irp - Pointer to the I/O Request Packet.

Return Value:

    None.

--*/
{
    PIO_STACK_LOCATION irpSp;
    PIO_STACK_LOCATION nextIrpSp;
    irpSp = IoGetCurrentIrpStackLocation(Irp);
    nextIrpSp = IoGetNextIrpStackLocation(Irp);
    RtlCopyMemory( nextIrpSp, irpSp, FIELD_OFFSET(IO_STACK_LOCATION, CompletionRoutine));
    nextIrpSp->Control = 0;
}

可以看到,就是从IO_STACK_LOCATION结构体CompletionRoutine这个成员以前的所有数据复制,而不是全部复制。

注意:FILE_OFFSET

这里有一个宏FILE_OFFSET,定义如下:

#define FIELD_OFFSET(type, field)    ((LONG)(LONG_PTR)&(((type *)0)->field))

其功能就是计算某个成员相对其结构体的偏移量。

IoSetCompletionRoutine

VOID
IoSetCompletionRoutine(
    _In_ PIRP Irp,
    _In_opt_ PIO_COMPLETION_ROUTINE CompletionRoutine,
    _In_opt_ __drv_aliasesMem PVOID Context,
    _In_ BOOLEAN InvokeOnSuccess,
    _In_ BOOLEAN InvokeOnError,
    _In_ BOOLEAN InvokeOnCancel
    )
//++
//
// Routine Description:
//
//     This routine is invoked to set the address of a completion routine which
//     is to be invoked when an I/O packet has been completed by a lower-level
//     driver.
//
// Arguments:
//
//     Irp - Pointer to the I/O Request Packet itself.
//
//     CompletionRoutine - Address of the completion routine that is to be
//         invoked once the next level driver completes the packet.
//
//     Context - Specifies a context parameter to be passed to the completion
//         routine.
//
//     InvokeOnSuccess - Specifies that the completion routine is invoked when the
//         operation is successfully completed.
//
//     InvokeOnError - Specifies that the completion routine is invoked when the
//         operation completes with an error status.
//
//     InvokeOnCancel - Specifies that the completion routine is invoked when the
//         operation is being canceled.
//
// Return Value:
//
//     None.
//
//--
{
    PIO_STACK_LOCATION irpSp;
    NT_ASSERT( (InvokeOnSuccess || InvokeOnError || InvokeOnCancel) ? (CompletionRoutine != NULL) : TRUE );
    irpSp = IoGetNextIrpStackLocation(Irp);
    irpSp->CompletionRoutine = CompletionRoutine;
    irpSp->Context = Context;
    irpSp->Control = 0;

    if (InvokeOnSuccess) {
        irpSp->Control = SL_INVOKE_ON_SUCCESS;
    }

    if (InvokeOnError) {
        irpSp->Control |= SL_INVOKE_ON_ERROR;
    }

    if (InvokeOnCancel) {
        irpSp->Control |= SL_INVOKE_ON_CANCEL;
    }
}

通过分析IoSetCompletionRoutine这个函数,可以看到设置的完成函数并不是设置本层的STACK_LOCATION,而是下一层的。因为设置完成函数就是为了等待下层IRP完成的返回。
至于是什么时候完成,可以通过分析IoCompleteRequest,这里就不再深究了。

HID人机交互QQ群:564808376    UAC音频QQ群:218581009    UVC相机QQ群:331552032    BOT&UASP大容量存储QQ群:258159197    STC-USB单片机QQ群:315457461    USB技术交流QQ群2:580684376    USB技术交流QQ群:952873936   

0 篇笔记 写笔记

USB总线FDO调用 IoInvalidateDeviceRelations通知PNP有新的设备后子设备收到的IRP
在USB FDO总线驱动中,创建了子设备PDO后,调用通知PNP管理器设备树发生了变化。这时系统会重新获取子设备关系树,然后对子设备进行信息收集,并启用。具体的过程如下:->FDO:IRP_MN_QUERY_DEVICE_RELATIONS PDO:IRP_MN_QUERY_ID......
USBCCGP 功能设备驱动FDO创建
回到入口函数DriverEntry,驱动扩展结构体的AddDevice函数指针被置为USBCCGP_AddDevice。这个函数指针是WDM驱动功能设备创建和扩展数据初始的回调函数。usbccgp.cNTSTATUSNTAPIDriverEntry( PDRIVER_OBJECT D......
USBCCGP FDO的启动
USBCCGP FDO的启动会执行主功能号为IRP_MJ_PNP,次功功能号IRP_MN_START_DEVICE的IRP。其函数调用关系如下:USBCCGP_Dispatch FDO_Dispatch FDO_HandlePnp ......
USBIP 创建FDO设备和子设备PDO
设备创建由add_vdev函数实现,具体过程为:使用vdev_create创建FDO设备建立自己的设备链表将创建的FDO和PDO使用IoAttachDeviceToDeviceStack函数关联最后根据设备类型进行初始化设备层级及设备成员指针链表如下:static PAGEABLE NTST......
USBIP FDO和PDO设备类型及结构体大小
设备类型typedef enum { VDEV_ROOT,//虚拟根设备FDO VDEV_CPDO,//虚拟USB控制器PDO VDEV_VHCI,//USB控制器FDO VDEV_HPDO,//USB根HUB PDO VDEV_VHUB, //USB根HUB......
USBIP 虚拟根设备(VDEV_ROOT)FDO的初始化过程
AddDeviceUSBIP使用devcon安装根设备驱动后,会创建其对应的PDO,这时系统会加载我们的驱动调用AddDevice函数创建PDO,进入进行堆栈。devcon.exe install vaudio.inf "USBIPWIN oot"我们在之前的创建设备Add......
USBIP 虚拟控制器设备(VDEV_VHCI)FDO的初始化过程
虚拟ROOT总线FDO创建了虚拟USB控制器PDO之后,系统通过各种IRP_MJ_PNP收集完物理设备的信息之后,开始根据其硬件ID进行设备驱动批配,批配成功后,装载驱动并调用其AddDevice之后,开始FDO的创建过程。通过前面的可知,USBIP实现的根驱动,USB控制器、HUB和设备PDO的S......
USBIP 虚拟集线器FDO子设备的管理
IRP_MN_QUERY_DEVICE_RELATIONS这得从IRP_MN_QUERY_DEVICE_RELATIONS来谈起,好像有点看的不是很明白。先把上一节的代码复制过来,看一下:static PAGEABLE NTSTATUSget_bus_relations_vhub(pvhub_d......
USBIP 虚拟集线器FDO(VDEV_VHUB )的初始化
AddDevice执行vhci_add_device,返回的设备类型为VDEV_VHUB,集线器HUB的FDO类型。然后初始化HUB的FDO,使用init_dev_vhub(vdev);函数实现static PAGEABLE voidinit_dev_vhub(pvdev_t vdev){ ......
HidClassFDO_DispatchRequest
NTSTATUSHidClassFDO_DispatchRequest( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp){ PHIDCLASS_COMMON_DEVICE_EXTENSION CommonDeviceExtens......
HID.dll与整个HID驱动程序架构的关系揭密
这里我们首先回顾一下HID相关驱动之间的调用关系图。USB HID设备主要由包括以下几个驱动:HidClass.sys 这个驱动是HID驱动的类驱动,其实就是一个DLL,用于处理USB HID驱动通用功能。HIDUSB.sys 这个驱动是HID驱动的miniPort驱动,即我们常说的迷你小端口......
关注公众号
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!