reactos操作系统实现(95)

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",

#027PortDeviceExtension->KeyboardInterrupt.Affinity);

相关文章

导入moment 使用方式 年月日,时分秒 星期几 相对时间 7天后 2小时后 明天 将毫秒转换成年月日
@ 一、前言 为什么介绍redux-actions呢? 第一次见到主要是接手公司原有的项目,发现有之前的大佬在处理...
十大React Hook库 原文地址:https://dev.to/bornfightcompany/top-10-react-hook-libraries-4065 原文...
React生命周期 React的生命周期从广义上分为挂载、渲染、卸载三个阶段,在React的整个生命周期中提供很...
React虚拟DOM的理解 Virtual DOM是一棵以JavaScript对象作为基础的树,每一个节点可以将其称为VNode,用...
React中JSX的理解 JSX是快速生成react元素的一种语法,实际是React.createElement(component, props, ....