即插即用(Plug and Play,PnP)是计算机系统I/O设备与部件配置的应用技术。PnP就是指插入就可以使用,不需要进行任何的硬件配置。其实还是需要安装相应的驱动程序才可以使用的。随着人们对计算机使用多样化,不断地需要添加各种各样的硬件卡到计算机的主板上,这样就需要配置硬件卡的中断、I/O所占用的资源,才能正常工作。这个过程是非常复杂的,经常需要手工配置,还需要查看计算机那里中断是空闲的,那些I/O端口是没有占用的。使用PnP技术以后,只需要操作系统做统一分配就行了。下面就来分析PnP初始化的实现代码:
#001 VOID INIT_FUNCTION
#002 PnpInit(VOID)
#003 {
#004 PDEVICE_OBJECT Pdo;
#005 NTSTATUS Status;
#006
#007 DPRINT("PnpInit()/n");
#008
初始化IO设备树锁。
#009 KeInitializeSpinLock(&IopDeviceTreeLock);
#010
分配总线类型的GUID列表。
#011 /* Initialize the Bus Type GUID List */
#012 IopBusTypeGuidList = ExAllocatePool(PagedPool, sizeof(IO_BUS_TYPE_GUID_LIST));
#013 if (!IopBusTypeGuidList) {
#014 DPRINT1("ExAllocatePool() failed/n");
#015 KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, STATUS_NO_MEMORY, 0, 0, 0);
#016 }
#017
#018 RtlZeroMemory(IopBusTypeGuidList, sizeof(IO_BUS_TYPE_GUID_LIST));
#019 ExInitializeFastMutex(&IopBusTypeGuidList->Lock);
#020
初始化即插即用的事件通知支持。
#021 /* Initialize PnP-Event notification support */
#022 Status = IopInitPlugPlayEvents();
#023 if (!NT_SUCCESS(Status))
#024 {
#025 DPRINT1("IopInitPlugPlayEvents() failed/n");
#026 KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, Status, 0, 0, 0);
#027 }
#028
创建一个根设备驱动程序节点,保存在IopRootDriverObject里面。
#029 /*
#030 * Create root device node
#031 */
#032
#033 Status = IopCreateDriver(NULL, PnpDriverInitializeEmpty, NULL, 0, 0, &IopRootDriverObject);
#034 if (!NT_SUCCESS(Status))
#035 {
#036 DPRINT1("IoCreateDriverObject() failed/n");
#037 KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, Status, 0, 0, 0);
#038 }
#039
创建一个文件设备控制器对象保存到根驱动程序节点里。
#040 Status = IoCreateDevice(IopRootDriverObject, 0, NULL, FILE_DEVICE_CONTROLLER,
#041 0, FALSE, &Pdo);
#042 if (!NT_SUCCESS(Status))
#043 {
#044 DPRINT1("IoCreateDevice() failed/n");
#045 KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, Status, 0, 0, 0);
#046 }
#047
创建一个文件设备控制器对象节点。
#048 Status = IopCreateDeviceNode(NULL, Pdo, NULL, &IopRootDeviceNode);
#049 if (!NT_SUCCESS(Status))
#050 {
#051 DPRINT1("Insufficient resources/n");
#052 KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, Status, 0, 0, 0);
#053 }
#054
设置这个文件设备节点名称为HTREE//ROOT//。
#055 if (!RtlCreateUnicodeString(&IopRootDeviceNode->InstancePath,
#056 L"HTREE//ROOT//0"))
#057 {
#058 DPRINT1("Failed to create the instance path!/n");
#059 KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, STATUS_NO_MEMORY, 0, 0, 0);
#060 }
#061
报告这个设备到用户模式的PNP管理器。
#062 /* Report the device to the user-mode pnp manager */
#063 IopQueueTargetDeviceEvent(&GUID_DEVICE_ARRIVAL,
#064 &IopRootDeviceNode->InstancePath);
#065
#066 IopRootDeviceNode->PhysicalDeviceObject->Flags |= DO_BUS_ENUMERATED_DEVICE;
#067 PnpRootDriverEntry(IopRootDriverObject, NULL);
#068 IopRootDeviceNode->PhysicalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
#069 IopRootDriverObject->DriverExtension->AddDevice(
#070 IopRootDriverObject,
#071 IopRootDeviceNode->PhysicalDeviceObject);
#072
下面开始把Freeloader检测到的硬件信息移到注册表SYSTEM/CurrentControlSet/Root/键里。
#073 /* Move information about devices detected by Freeloader to SYSTEM/CurrentControlSet/Root/ */
#074 Status = IopUpdateRootKey();
#075 if (!NT_SUCCESS(Status))
#076 {
#077 DPRINT1("IopUpdateRootKey() failed/n");
#078 KeBugCheckEx(PHASE1_INITIALIZATION_FAILED, Status, 0, 0, 0);
#079 }
#080 }