当总线检测到了硬件设备插入之后,就会产生电信号的中断,导致驱动程序调用IoInvalidDeviceRelations
,这个时候PNP管理器就会向总线驱动发送IRP_MN_QUERY_DEVICE_RELATION
的请求,总线驱动接收到这个请求之后,就会开始枚举自己的子设备,随着IRP_MN_QUERY_DEVICE_RELATION
的返回值返回子设备信息。
PNP管理器查询到了子设备信息之后,针对每个子设备,开始调用IRP_MN_QUERY_ID
, IRP_MN_QUERY_DEVICE_TEXT
来获取设备的描述信息。
接着PNP会继续调用IRP_MN_QUERY_RESOURCES
和 IRP_MN_QUERY_RESOURCE_REQUIREMENTS
来获取资源,然后加载设备驱动,并调用IRP_MN_FILTER_RESOURCE_REQUIREMENTS
来过滤自己的资源信息。
一切事情准备妥当之后,开始调用IRP_MN_START_DEVICE
启动设备,这个IRP将会携带PNP管理器给设备分配的资源信息,那么这个资源是什么样的呢?
我们看一下IRP_MN_START_DEVICE
这个IRP附带的参数信息。
VOID
NTAPI
PnpStartDevice()
{
//...
RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
Stack.MajorFunction = IRP_MJ_PNP;
Stack.MinorFunction = IRP_MN_START_DEVICE;
Stack.Parameters.StartDevice.AllocatedResources =
DeviceNode->ResourceList;
Stack.Parameters.StartDevice.AllocatedResourcesTranslated =
DeviceNode->ResourceListTranslated;
Status = IopSynchronousCall(DeviceObject, &Stack, &Dummy);
//...
}
从这里可以看到有两个资源:
Stack.Parameters.StartDevice.AllocatedResources
适应硬件使用的资源信息。Stack.Parameters.StartDevice.AllocatedResourcesTranslated
适应系统使用资源信息。其中资源的结构如下:
typedef struct _CM_RESOURCE_LIST {
ULONG Count;
CM_FULL_RESOURCE_DESCRIPTOR List[1];
} CM_RESOURCE_LIST, *PCM_RESOURCE_LIST;
typedef struct _CM_FULL_RESOURCE_DESCRIPTOR {
INTERFACE_TYPE InterfaceType;
ULONG BusNumber;
CM_PARTIAL_RESOURCE_LIST PartialResourceList;
} CM_FULL_RESOURCE_DESCRIPTOR, *PCM_FULL_RESOURCE_DESCRIPTOR;
typedef struct _CM_PARTIAL_RESOURCE_LIST {
USHORT Version;
USHORT Revision;
ULONG Count;
CM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptors[1];
} CM_PARTIAL_RESOURCE_LIST, *PCM_PARTIAL_RESOURCE_LIST;
typedef struct _CM_PARTIAL_RESOURCE_DESCRIPTOR {
UCHAR Type;
UCHAR ShareDisposition;
USHORT Flags;
union {
struct {
PHYSICAL_ADDRESS Start;
ULONG Length;
} Generic;
struct {
PHYSICAL_ADDRESS Start;
ULONG Length;
} Port;
struct {
#if defined(NT_PROCESSOR_GROUPS)
USHORT Level;
USHORT Group;
#else
ULONG Level;
#endif
ULONG Vector;
KAFFINITY Affinity;
} Interrupt;
// This member exists only on Windows Vista and later
struct {
union {
struct {
#if defined(NT_PROCESSOR_GROUPS)
USHORT Group;
#else
USHORT Reserved;
#endif
USHORT MessageCount;
ULONG Vector;
KAFFINITY Affinity;
} Raw;
struct {
#if defined(NT_PROCESSOR_GROUPS)
USHORT Level;
USHORT Group;
#else
ULONG Level;
#endif
ULONG Vector;
KAFFINITY Affinity;
} Translated;
};
} MessageInterrupt;
struct {
PHYSICAL_ADDRESS Start;
ULONG Length;
} Memory;
struct {
ULONG Channel;
ULONG Port;
ULONG Reserved1;
} Dma;
struct {
ULONG Channel;
ULONG RequestLine;
UCHAR TransferWidth;
UCHAR Reserved1;
UCHAR Reserved2;
UCHAR Reserved3;
} DmaV3;
struct {
ULONG Data[3];
} DevicePrivate;
struct {
ULONG Start;
ULONG Length;
ULONG Reserved;
} BusNumber;
struct {
ULONG DataSize;
ULONG Reserved1;
ULONG Reserved2;
} DeviceSpecificData;
// The following structures provide support for memory-mapped
// IO resources greater than MAXULONG
struct {
PHYSICAL_ADDRESS Start;
ULONG Length40;
} Memory40;
struct {
PHYSICAL_ADDRESS Start;
ULONG Length48;
} Memory48;
struct {
PHYSICAL_ADDRESS Start;
ULONG Length64;
} Memory64;
struct {
UCHAR Class;
UCHAR Type;
UCHAR Reserved1;
UCHAR Reserved2;
ULONG IdLowPart;
ULONG IdHighPart;
} Connection;
} u;
} CM_PARTIAL_RESOURCE_DESCRIPTOR, *PCM_PARTIAL_RESOURCE_DESCRIPTOR;
从这里看CM_PARTIAL_RESOURCE_DESCRIPTOR
这个才是一个资源的真实描述,其他的,只是对于这个资源的管理信息,这个结构可以导致描述如下:
最终的结构信息是最右所示的信息,如果我们查看这个结构体的话,类似依次从左往右展开。
在IRP_MN_START_DEVICE
响应函数中,我们就可以提取这些资源了,资源的提取伪代码如下:
NTSTATUS
StartDevice(PDEVICE_OBJECT fdo, PCM_PARTIAL_RESOURCE_LIST raw, PCM_PARTIAL_RESOURCE_LIST translated)
{
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
PCM_PARTIAL_RESOURCE_DESCRIPTOR resource = translated->PartialDescriptors;
ULONG nres = translated->Count;
//
for (ULONG i = 0; i < nres; ++i, ++resource)
{
switch (resource->Type)
{
case CmResourceTypePort:
//
break;
case CmResourceTypeInterrupt:
//
break;
case CmResourceTypeMemory:
//
break;
case CmResourceTypeDma:
//
break;
}
}
//
IoSetDeviceInterfaceState(&pdx->ifname, TRUE);
}
总共包括四种资源,分别为:
资源类型 | 处理概述 |
---|---|
Port | 可能映射端口范围;应在设备扩展中保存端口范围基址 |
Memory | 映射内存范围;应在设备扩展中保存内存范围基址 |
Dma | 调用IoGetDmaAdapter函数创建适配器对象 |
Interrupt | 调用IoConnectInterrupt函数创建中断对象,中断对象指向ISR(中断服务例程) |
其中,Memory的传输需要CPU的参与,所以适合传输少量数据;而DMA使用的是DMA控制器,这样的话DMA控制器直接控制数据传输,无需CPU参与,使用大量的数据传输,MDA的模型如下: