在PNP管理器里,最重要的处理,就是即插即用消息。下面来分析键盘的即插即用消息处理函数,实现的代码如下:
#001 NTSTATUS NTAPI
#002 i8042Pnp(
#003 IN PDEVICE_OBJECT DeviceObject,
#004 IN PIRP Irp)
#005 {
#006 PIO_STACK_LOCATION Stack;
#007 ULONG MinorFunction;
#008 I8042_DEVICE_TYPE DeviceType;
#009 ULONG_PTR Information = 0;
#010 NTSTATUS Status;
#011
获取IRP的栈。
#012 Stack = IoGetCurrentIrpStackLocation(Irp);
获取IRP次功能代码。
#013 MinorFunction = Stack->MinorFunction;
获取设备类型。
#014 DeviceType = ((PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension)->Type;
#015
根据即插即用次功能代码处理。
#016 switch (MinorFunction)
#017 {
分配资源并启动一个设备。
#018 case IRP_MN_START_DEVICE: /* 0x00 */
#019 {
#020 TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_START_DEVICE/n");
#021
如果设备类型不为物理设备类型,就处理。
#022 /* Call lower driver (if any) */
#023 if (DeviceType != PhysicalDeviceObject)
#024 {
向前传送IRP,并等待回应。
#025 Status = ForwardIrpAndWait(DeviceObject, Irp);
如果回应IRP成功,就调用i8042PnpStartDevice函数来分配资源。
#026 if (NT_SUCCESS(Status))
#027 Status = i8042PnpStartDevice(
#028 DeviceObject,
#029 Stack->Parameters.StartDevice.AllocatedResources,
#030 Stack->Parameters.StartDevice.AllocatedResourcesTranslated);
#031 }
#032 else
#033 Status = STATUS_SUCCESS;
#034 break;
#035 }
查询是否有子设备。
#036 case IRP_MN_QUERY_DEVICE_RELATIONS: /* (optional) 0x07 */
#037 {
#038 switch (Stack->Parameters.QueryDeviceRelations.Type)
#039 {
PNP 管理器向设备发送一个带有 BusRelations 码的 IRP_MN_QUERY_DEVICE_RELATIONS 的请求来获得设备的子设备列表,这里回应的子设备列表为0个。
#040 case BusRelations:
#041 {
#042 PDEVICE_RELATIONS DeviceRelations;
#043
#044 TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / BusRelations/n");
#045 DeviceRelations = ExAllocatePool(PagedPool, sizeof(DEVICE_RELATIONS));
#046 if (DeviceRelations)
#047 {
#048 DeviceRelations->Count = 0;
#049 Information = (ULONG_PTR)DeviceRelations;
#050 Status = STATUS_SUCCESS;
#051 }
#052 else
#053 Status = STATUS_INSUFFICIENT_RESOURCES;
#054 break;
#055 }
这里处理即插即用的删除子设备的IRP。
#056 case RemovalRelations:
#057 {
#058 TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / RemovalRelations/n");
#059 return ForwardIrpAndForget(DeviceObject, Irp);
#060 }
缺省的IRP处理。
#061 default:
#062 ERR_(I8042PRT, "IRP_MJ_PNP / IRP_MN_QUERY_DEVICE_RELATIONS / Unknown type 0x%lx/n",
#063 Stack->Parameters.QueryDeviceRelations.Type);
#064 ASSERT(FALSE);
#065 return ForwardIrpAndForget(DeviceObject, Irp);
#066 }
#067 break;
#068 }
过滤系统请求的资源。
#069 case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: /* (optional) 0x0d */
#070 {
#071 TRACE_(I8042PRT, "IRP_MJ_PNP / IRP_MN_FILTER_RESOURCE_REQUIREMENTS/n");
#072 /* Nothing to do */
#073 Status = Irp->IoStatus.Status;
#074 break;
#075 }
#076 default:
#077 {
#078 ERR_(I8042PRT, "IRP_MJ_PNP / unknown minor function 0x%x/n", MinorFunction);
#079 ASSERT(FALSE);
#080 return ForwardIrpAndForget(DeviceObject, Irp);
#081 }
#082 }
#083
IRP完成设置。
#084 Irp->IoStatus.Information = Information;
#085 Irp->IoStatus.Status = Status;
#086 IoCompleteRequest(Irp, IO_NO_INCREMENT);
#087 return Status;
#088 }
#089
接着来分析启动设备的消息,函数i8042PnpStartDevice的实现代码如下:
#001 static NTSTATUS
#002 i8042PnpStartDevice(
#003 IN PDEVICE_OBJECT DeviceObject,
#004 IN PCM_RESOURCE_LIST AllocatedResources,
#005 IN PCM_RESOURCE_LIST AllocatedResourcesTranslated)
#006 {
#007 PFDO_DEVICE_EXTENSION DeviceExtension;
#008 PPORT_DEVICE_EXTENSION PortDeviceExtension;
#009 PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor, ResourceDescriptorTranslated;
#010 INTERRUPT_DATA InterruptData;
#011 BOOLEAN FoundDataPort = FALSE;
#012 BOOLEAN FoundControlPort = FALSE;
#013 BOOLEAN FoundIrq = FALSE;
#014 ULONG i;
#015 NTSTATUS Status;
#016
#017 TRACE_(I8042PRT, "i8042PnpStartDevice(%p)/n", DeviceObject);
获取设备扩展对象。
#018 DeviceExtension = (PFDO_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
获取当前端口对象。
#019 PortDeviceExtension = DeviceExtension->PortDeviceExtension;
#020
#021 ASSERT(DeviceExtension->PnpState == dsStopped);
#022
即插即用管理器分配资源失败,因此直接返回。
#023 if (!AllocatedResources)
#024 {
#025 WARN_(I8042PRT, "No allocated resources sent to driver/n");
#026 return STATUS_INSUFFICIENT_RESOURCES;
#027 }
如果分配资源数量不对,就返回出错。
#028 if (AllocatedResources->Count != 1)
#029 {
#030 WARN_(I8042PRT, "Wrong number of allocated resources sent to driver/n");
#031 return STATUS_INSUFFICIENT_RESOURCES;
#032 }
判断分配资源的版本是否一样,如果不一样也需要返回出错。
#033 if (AllocatedResources->List[0].PartialResourceList.Version != 1
#034 || AllocatedResources->List[0].PartialResourceList.Revision != 1
#035 || AllocatedResourcesTranslated->List[0].PartialResourceList.Version != 1
#036 || AllocatedResourcesTranslated->List[0].PartialResourceList.Revision != 1)
#037 {
#038 WARN_(I8042PRT, "Revision mismatch: %u.%u != 1.1 or %u.%u != 1.1/n",
#039 AllocatedResources->List[0].PartialResourceList.Version,
#040 AllocatedResources->List[0].PartialResourceList.Revision,
#041 AllocatedResourcesTranslated->List[0].PartialResourceList.Version,
#042 AllocatedResourcesTranslated->List[0].PartialResourceList.Revision);
#043 return STATUS_REVISION_MISMATCH;
#044 }
#045
获取操作系统分配资源,比如端口地址和使用内存,还有中断号等等。
#046 /* Get Irq and optionally control port and data port */
#047 for (i = 0; i < AllocatedResources->List[0].PartialResourceList.Count; i++)
#048 {
资源描述结构。
#049 ResourceDescriptor = &AllocatedResources->List[0].PartialResourceList.PartialDescriptors[i];
#050 ResourceDescriptorTranslated = &AllocatedResourcesTranslated->List[0].PartialResourceList.PartialDescriptors[i];
根据资源类型来处理。
#051 switch (ResourceDescriptor->Type)
#052 {
分配的端口资源。
#053 case CmResourceTypePort:
#054 {
找到端口资源。
#055 if (ResourceDescriptor->u.Port.Length == 1)
#056 {
#057 /* We assume that the first ressource will
#058 * be the control port and the second one
#059 * will be the data port...
#060 */
先判断是否数据端口。
#061 if (!FoundDataPort)
#062 {
保存系统分配的数据端口地址。
#063 PortDeviceExtension->DataPort = ULongToPtr(ResourceDescriptor-
#064 >u.Port.Start.u.LowPart);
#065 INFO_(I8042PRT, "Found data port: %p/n", PortDeviceExtension->DataPort);
#066 FoundDataPort = TRUE;
#067 }
#068 else if (!FoundControlPort)
#069 {
保存系统分配的控制命令端口地址。
#070 PortDeviceExtension->ControlPort = ULongToPtr(ResourceDescriptor-
#071 >u.Port.Start.u.LowPart);
#072 INFO_(I8042PRT, "Found control port: %p/n", PortDeviceExtension->ControlPort);
#073 FoundControlPort = TRUE;
#074 }
#075 else
#076 {
其它是分配错误的端口地址。
#077 WARN_(I8042PRT, "Too much I/O ranges provided: 0x%lx/n", ResourceDescriptor-
#078 >u.Port.Length);
#079 return STATUS_INVALID_PARAMETER;
#080 }
#081 }
#082 else
#083 WARN_(I8042PRT, "Invalid I/O range length: 0x%lx/n", ResourceDescriptor->u.Port.Length);
#084 break;
#085 }
这里处理系统分配的中断资源。
#086 case CmResourceTypeInterrupt:
#087 {
如果已经分配了中断,就返回出错。
#088 if (FoundIrq)
#089 return STATUS_INVALID_PARAMETER;
保存中断资源。
#090 InterruptData.Dirql = (KIRQL)ResourceDescriptorTranslated->u.Interrupt.Level;
#091 InterruptData.Vector = ResourceDescriptorTranslated->u.Interrupt.Vector;
#092 InterruptData.Affinity = ResourceDescriptorTranslated->u.Interrupt.Affinity;
中断模式。
#093 if (ResourceDescriptorTranslated->Flags & CM_RESOURCE_INTERRUPT_LATCHED)
#094 InterruptData.InterruptMode = Latched;
#095 else
#096 InterruptData.InterruptMode = LevelSensitive;
中断源是否共享。
#097 InterruptData.ShareInterrupt = (ResourceDescriptorTranslated->ShareDisposition == CmResourceShareShared);
#098 INFO_(I8042PRT, "Found irq resource: %lu/n", ResourceDescriptor->u.Interrupt.Level);
#099 FoundIrq = TRUE;
#100 break;
#101 }
#102 default:
#103 WARN_(I8042PRT, "Unknown resource descriptor type 0x%x/n", ResourceDescriptor->Type);
#104 }
#105 }
#106
如果没有分配中断资源,就返回出错。
#107 if (!FoundIrq)
#108 {
#109 WARN_(I8042PRT, "Interrupt resource was not found in allocated resources list/n");
#110 return STATUS_INSUFFICIENT_RESOURCES;
#111 }
#112 else if (DeviceExtension->Type == Keyboard && (!FoundDataPort || !FoundControlPort))
#113 {
如果是键盘类型,但又没有分配数据端口和命令控制端口资源,也返回出错。
#114 WARN_(I8042PRT, "Some required resources were not found in allocated resources list/n");
#115 return STATUS_INSUFFICIENT_RESOURCES;
#116 }
#117 else if (DeviceExtension->Type == Mouse && (FoundDataPort || FoundControlPort))
#118 {
如果是鼠标类型,但又没有分配数据端口和命令控制端口资源,也返回出错。
#119 WARN_(I8042PRT, "Too much resources were provided in allocated resources list/n");
#120 return STATUS_INVALID_PARAMETER;
#121 }
#122
根据不同类型来处理。
#123 switch (DeviceExtension->Type)
#124 {
#125 case Keyboard:
#126 {
键盘类型处理,调用函数StartProcedure来处理键盘中断设置,并启动键盘。
#127 RtlCopyMemory(
#128 &PortDeviceExtension->KeyboardInterrupt,
#129 &InterruptData,
#130 sizeof(INTERRUPT_DATA));
#131 PortDeviceExtension->Flags |= KEYBOARD_STARTED;
#132 Status = StartProcedure(PortDeviceExtension);
#133 break;
#134 }
#135 case Mouse:
#136 {
鼠标类型处理,调用函数StartProcedure来处理鼠标中断设置,并启动鼠标。
#137 RtlCopyMemory(
#138 &PortDeviceExtension->MouseInterrupt,
#139 &InterruptData,
#140 sizeof(INTERRUPT_DATA));
#141 PortDeviceExtension->Flags |= MOUSE_STARTED;
#142 Status = StartProcedure(PortDeviceExtension);
#143 break;
#144 }
#145 default:
#146 {
#147 WARN_(I8042PRT, "Unknown FDO type %u/n", DeviceExtension->Type);
#148 ASSERT(!(PortDeviceExtension->Flags & KEYBOARD_CONNECTED) || !(PortDeviceExtension->Flags & MOUSE_CONNECTED));
#149 Status = STATUS_INVALID_DEVICE_REQUEST;
#150 }
#151 }
#152
这里设置即插即用初始化状态成功完成。
#153 if (NT_SUCCESS(Status))
#154 DeviceExtension->PnpState = dsStarted;
#155
#156 return Status;
#157 }
下面来分析函数StartProcedure的实现,代码如下:
#001 static NTSTATUS
#002 StartProcedure(
#003 IN PPORT_DEVICE_EXTENSION DeviceExtension)
#004 {
#005 NTSTATUS Status;
#006 UCHAR FlagsToDisable = 0;
#007 UCHAR FlagsToEnable = 0;
#008
如果检查没有数据端口,就立即返回。
#009 if (DeviceExtension->DataPort == 0)
#010 {
#011 /* Unable to do something at the moment */
#012 return STATUS_SUCCESS;
#013 }
#014
如果没有发现键盘或鼠标设备存在,就尽量尝试加载键盘或鼠标。
#015 if (!(DeviceExtension->Flags & (KEYBOARD_PRESENT | MOUSE_PRESENT)))
#016 {
#017 /* Try to detect them */
#018 TRACE_(I8042PRT, "Check if the controller is really a i8042/n");
检查设备是否存在。
#019 Status = i8042BasicDetect(DeviceExtension);
#020 if (!NT_SUCCESS(Status))
#021 {
#022 WARN_(I8042PRT, "i8042BasicDetect() failed with status 0x%08lx/n", Status);
找不到设备,就返回。
#023 return STATUS_UNSUCCESSFUL;
#024 }
#025
#026 /* First detect the mouse and then the keyboard!
#027 If we do it the other way round, some systems throw away settings like the keyboard translation, when detecting the mouse.
#028
如果不是首次安装模式,就检测鼠标是否存在。
#029 Don't detect the mouse if we're in 1st stage setup! */
#030 if(!IsFirstStageSetup())
#031 {
#032 TRACE_(I8042PRT, "Detecting mouse/n");
#033 i8042DetectMouse(DeviceExtension);
#034 }
#035
检测键盘。
#036 TRACE_(I8042PRT, "Detecting keyboard/n");
#037 i8042DetectKeyboard(DeviceExtension);
#038
#039 INFO_(I8042PRT, "Keyboard present: %s/n", DeviceExtension->Flags & KEYBOARD_PRESENT ? "YES" : "NO");
#040 INFO_(I8042PRT, "Mouse present : %s/n", DeviceExtension->Flags & MOUSE_PRESENT ? "YES" : "NO");
#041 }
#042
设置键盘的中断处理。
#043 /* Connect interrupts */
#044 if (DeviceExtension->Flags & KEYBOARD_PRESENT &&
#045 DeviceExtension->Flags & KEYBOARD_CONNECTED &&
#046 DeviceExtension->Flags & KEYBOARD_STARTED &&
#047 !(DeviceExtension->Flags & KEYBOARD_INITIALIZED))
#048 {
调用函数i8042ConnectKeyboardInterrupt来设置键盘中断处理函数。
#049 /* Keyboard is ready to be initialized */
#050 Status = i8042ConnectKeyboardInterrupt(DeviceExtension->KeyboardExtension);
#051 if (NT_SUCCESS(Status))
#052 {
#053 DeviceExtension->Flags |= KEYBOARD_INITIALIZED;
#054 FlagsToDisable |= CCB_KBD_DISAB;
#055 FlagsToEnable |= CCB_KBD_INT_ENAB;
#056 }
#057 }
#058
设置鼠标的中断处理。
#059 if (DeviceExtension->Flags & MOUSE_PRESENT &&
#060 DeviceExtension->Flags & MOUSE_CONNECTED &&
#061 DeviceExtension->Flags & MOUSE_STARTED &&
#062 !(DeviceExtension->Flags & MOUSE_INITIALIZED))
#063 {
调用函数i8042ConnectKeyboardInterrupt来设置鼠标中断处理。
#064 /* Mouse is ready to be initialized */
#065 Status = i8042ConnectMouseInterrupt(DeviceExtension->MouseExtension);
#066 if (NT_SUCCESS(Status))
#067 {
#068 DeviceExtension->Flags |= MOUSE_INITIALIZED;
#069 FlagsToDisable |= CCB_MOUSE_DISAB;
#070 FlagsToEnable |= CCB_MOUSE_INT_ENAB;
#071 }
#072 }
#073
如果设置中断处理成功,就打开中断标志。
#074 if (FlagsToEnable)
#075 Status = EnableInterrupts(DeviceExtension, FlagsToDisable, FlagsToEnable);
#076 else
#077 Status = STATUS_SUCCESS;
#078
#079 return Status;
#080 }
下面来分析中断设置函数i8042ConnectKeyboardInterrupt,代码如下:
#001 static NTSTATUS
#002 i8042ConnectKeyboardInterrupt(
#003 IN PI8042_KEYBOARD_EXTENSION DeviceExtension)
#004 {
#005 PPORT_DEVICE_EXTENSION PortDeviceExtension;
#006 KIRQL DirqlMax;
#007 NTSTATUS Status;
#008
#009 TRACE_(I8042PRT, "i8042ConnectKeyboardInterrupt()/n");
#010
获取设备端口。
#011 PortDeviceExtension = DeviceExtension->Common.PortDeviceExtension;
取得最大中断级别。
#012 DirqlMax = MAX(
#013 PortDeviceExtension->KeyboardInterrupt.Dirql,
#014 PortDeviceExtension->MouseInterrupt.Dirql);
#015
#016 INFO_(I8042PRT, "KeyboardInterrupt.Vector %lu/n",
#017 PortDeviceExtension->KeyboardInterrupt.Vector);
#018 INFO_(I8042PRT, "KeyboardInterrupt.Dirql %lu/n",
#019 PortDeviceExtension->KeyboardInterrupt.Dirql);
#020 INFO_(I8042PRT, "KeyboardInterrupt.DirqlMax %lu/n",
#021 DirqlMax);
#022 INFO_(I8042PRT, "KeyboardInterrupt.InterruptMode %s/n",
#023 PortDeviceExtension->KeyboardInterrupt.InterruptMode == LevelSensitive ? "LevelSensitive" : "Latched");
#024 INFO_(I8042PRT, "KeyboardInterrupt.ShareInterrupt %s/n",
#025 PortDeviceExtension->KeyboardInterrupt.ShareInterrupt ? "yes" : "no");
#026 INFO_(I8042PRT, "KeyboardInterrupt.Affinity 0x%lx/n",
#027 PortDeviceExtension->KeyboardInterrupt.Affinity);