USBCCGP FDO的启动
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
,这里就不再深究了。