windows驱动开发-PCI和中断(三)

2024-05-15 07:52
文章标签 windows 开发 驱动 中断 pci

本文主要是介绍windows驱动开发-PCI和中断(三),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在前面我们用一个实际的案例来说明了INTx中断是如何编程的,但是现在几乎已经很少遇到INTx中断了,基本上都是MSI和MSIX中断。

MSI和MSIX

PCI 2.2 规范中引入了 (MSI) 的消息信号中断,作为基于线路的中断的替代方法。 使用 MSI 的设备通过将值写入特定内存地址来触发中断,而不是使用专用引脚触发中断。 PCI 3.0 定义了一种扩展形式的 MSI,称为 MSI-X,可实现更高的可编程性。 Windows Vista 和更高版本的 Windows 支持 MSI 和 MSI-X。 单个设备可以同时支持 MSI 和 MSI-X。 对于此类设备,操作系统将自动使用 MSI-X。

中断消息是设备写入特定地址以触发中断的特定值。 与基于行(边带信号)的中断不同,消息信号中断具有边缘语义。 设备发送消息,但未收到任何硬件确认,说明已收到中断。

对于 PCI 2.2,消息由地址和部分不透明的 16 位值组成。 为每个设备分配一个地址。 若要发送多条消息,设备可以使用消息值的低 4 位来区分消息。 因此,对于 PCI 2.2,设备最多可以支持 16 条消息。

对于 PCI 3.0,消息由地址和不透明的 32 位值组成。 每封不同的邮件都有其自己的唯一地址。 与 PCI 2.2 不同,设备不会修改值。 对于 PCI 3.0,设备最多可以支持 2,048 条不同的消息。 支持 PCI 3.0 MSI-X 的设备具有一个动态可编程的硬件表,其中包含设备中每个中断源的条目。 可以使用分配给设备的消息之一对此表中的每个条目进行编程,并且可以独立屏蔽。 驱动程序可以将中断消息的编程更改为表条目,以及某个条目是否已被屏蔽。 

驱动程序可以注册单个 InterruptMessageService 例程,该例程处理所有可能的消息或每条消息的单个 InterruptService 例程。

驱动程序可以处理设备发送的 MSI,如下所示:

  • 在驱动程序安装过程中,在注册表中启用 MSI。 还可以使用注册表指定要为设备分配的消息数;
  • (可选)通过响应 IRP_MN_FILTER_RESOURCE_REQUIREMENTS 请求来增加中断消息数并保存一些每条消息设置;
  • 在用于IRP_MN_START_DEVICE的驱动程序调度例程中,调用 IoConnectInterruptEx 以注册 InterruptService 或 InterruptMessageService 例程来为设备的中断提供服务。 使用 ioConnectInterruptEx 的 CONNECT_FULLY_SPECIFIED 版本为特定消息注册 InterruptService 例程,或使用 ioConnectInterruptEx 的 CONNECT_MESSAGE_BASED 版本为所有消息注册单个 InterruptMessageService 例程;
  • 驱动程序不再打算处理来自设备的中断后,请在禁用设备的中断后调用 IoDisconnectInterruptEx  删除任何已注册的中断服务例程;

设计为使用多个消息的驱动程序应检查分配预期的消息数。 如果即插即用 (PnP) 管理器无法分配请求的消息数,它会只向设备分配一条消息。 驱动程序可以通过以下方式之一检查实际分配的消息数:

  • PnP 管理器在其原始资源描述符列表中报告分配的消息数。 
  • 当 IoConnectInterruptEx 返回时,它将 Parameters-MessageBased.ConnectContext.InterruptMessageTable-MessageCount>> 设置为分配的消息数;
注册表中启用MSI

若要 (MSI) 接收消息信号中断,驱动程序的 INF 文件必须在安装期间在注册表中启用 MSI。 使用设备hardware key的Interrupt Management\MessageSignaledInterruptProperties子项启用 MSI 支持。

Interrupt Management\MessageSignaledInterruptProperties的 MSISupported 条目是一个REG_DWORD值,用于确定设备是否支持 MSI,将 MSISupported 设置为 1 以启用 MSI 支持。

还可以使用注册表指定要为其设备分配的最大 MSI 数。 Interrupt Management\MessageSignaledInterruptProperties 的 MessageNumberLimit 条目是一个REG_DWORD值,指定要分配的最大 MSI 数。

对于从 PCI 2.2开始可用的多消息 MSI ,MessageNumberLimit 必须为 1、2、4、8 或 16。 对于从 PCI 3.0开始可用的 MSI-X 设备 MessageNumberLimit 可以是最多 2,048 个数字。

在驱动程序的 INF 文件中使用 INF AddReg 指令 在设备的硬件密钥下设置注册表项。 

下面的代码示例演示如何为设备设置Interrupt Management\MessageSignaledInterruptProperties 下的 MSISupported 条目。 请注意,在添加 MSISupported 值时,AddReg 指令会自动创建Interrupt Management和Interrupt Management\MessageSignaledInterruptProperties:

[mydevice.HW]
AddReg = mydevice_addreg[mydevice_addreg]
HKR,Interrupt Management\MessageSignaledInterruptProperties,MSISupported,0x00010001,1
中断资源描述符 

即插即用 (PnP) 管理器使用两个通道将中断消息分配给设备。

首先,PnP 管理器向驱动程序发送 一个IRP_MN_FILTER_RESOURCE_REQUIREMENTS 请求,其中包含它打算分配给设备的硬件资源列表,包括中断消息。 驱动程序可以修改此列表以更改中断消息数以及一些每条消息设置。 然后,在 PnP 管理器实际分配资源后,它会发送 IRP_MN_START_DEVICE 请求,并提供分配给驱动程序设备的硬件资源(包括中断消息)的完整列表。

IRP_MN_FILTER_RESOURCE_REQUIREMENTS请求提供IO_RESOURCE_DESCRIPTOR结构的列表。 如果设备具有 PCI 2.2 规范中定义的 MSI (消息信号中断) 功能结构,则 PnP 管理器将提供单个中断消息描述符。 如果设备具有 PCI 3.0 规范中定义的 MSI-X 功能结构,则 PnP 管理器为每个中断消息提供一个结构。 中断消息描述符的type = CmResourceTypeInterrupt , Flags = CM_RESOURCE_INTERRUPT_LATCHED |CM_RESOURCE_INTERRUPT_MESSAGE。 驱动程序还可以通过更改 结构的 u.Interrupt 成员来更改中断相关性等设置。 请注意,在使用 MSI 时,中断都具有相同的相关性,而使用 MSI-X 时,它们可能具有不同的相关性。 

对于 MSI,如 PCI 2.2 中定义, u.Interrupt.MaximumVector - u.Interrupt.MinimumVector + 1 是为设备分配的中断消息数。 驱动程序可以通过修改 u.Interrupt.MinimumVector 来更改中断消息数。 对于 MSI 中断消息, u.Interrupt.MaximumVector 始终CM_RESOURCE_INTERRUPT_MESSAGE_TOKEN。 若要分配 MessageCount 中断消息,请将 u.Interrupt.MinimumVector 设置为等于CM_RESOURCE_INTERRUPT_MESSAGE_TOKEN - MessageCount + 1。

对于 PCI 3.0 中定义的 MSI-X,驱动程序可以通过在列表中添加或删除条目来更改分配的中断消息数。 请注意,在响应 IRP_MN_START_DEVICE 请求时,不得随后删除以这种方式添加的中断消息资源。 对于 MSI-X,PnP 管理器为每个消息中断提供一个描述符,此描述 符的 u.Interrupt.MinimumVector 和 u.Interrupt.MaximumVector 成员都设置为CM_RESOURCE_INTERRUPT_MESSAGE_TOKEN。

即插即用管理器分配设备的所有硬件资源(包括中断消息)后,会将IRP_MN_START_DEVICE请求发送到驱动程序。 此请求提供两个 CM_PARTIAL_RESOURCE_DESCRIPTOR 结构列表,分别用于原始资源和已翻译的资源。 对于中断消息,PnP 管理器为每个分配的内存地址提供一个结构,type = 为CmResourceTypeInterrupt,Flags = CM_RESOURCE_INTERRUPT_LATCHED |CM_RESOURCE_INTERRUPT_MESSAGE。

请注意,使用 MSI 时,驱动程序仅接收一个中断资源描述符,因为所有消息共享相同的地址。 u.MessageInterrupt.Raw 的 MessageCount 成员可用于确定分配的消息数。 使用 MSI-X 时,驱动程序将接收每个中断消息的单独资源描述符。

在 Windows 8 中,操作系统不支持每个设备功能超过 2048 条中断消息的资源请求。 在 Windows 7 和 Windows Vista 中,操作系统不支持每个设备功能超过 910 条中断消息的资源请求。 如果设备驱动程序超出此限制,设备可能无法启动。 若要使驱动程序能够在包含许多逻辑处理器的计算机中运行,驱动程序应避免为每个处理器请求多个中断。

在系统重新平衡中断资源期间,PnP 管理器可能会要求驱动程序从资源要求列表中选择一组首选的备用中断资源。 但是,PnP 管理器不能始终向驱动程序分配驱动程序首选的资源。 因此,驱动程序必须容忍从资源要求列表中分配任何一组备用中断资源,而不会失败。 例如,为设备分配的消息中断数可能少于驱动程序请求的次数。 在最坏的情况下,驱动程序必须准备好仅使用一个基于线路的中断来操作设备。

动态配置 MSI-X

Windows Vista Service Pack 1 (SP1) 、Windows Server 2008 及更高版本的操作系统支持动态修改 MSI-X 中断消息的属性。 PCI 3.0 规范定义的 MSI-X.PCI 总线驱动程序公开GUID_MSIX_TABLE_CONFIG_INTERFACE接口,以允许 PCI 设备的驱动程序修改总线硬件中断表中的设置。

驱动程序通过向总线驱动程序发送 IRP_MN_QUERY_INTERFACE 请求来使用该接口, 其中 InterfaceType 参数等于 GUID_MSIX_TABLE_CONFIG_INTERFACE。 总线驱动程序提供指向 PCI_MSIX_TABLE_CONFIG_INTERFACE 结构的指针,该结构提供指向修改中断表的三个例程的指针:

  • SetTableEntry 将消息 ID 分配给硬件表条目;
  • MaskTableEntry 屏蔽与硬件表条目对应的中断;
  • UnmaskTableEntry 取消屏蔽对应于硬件表条目的中断;
PCI_MSIX_MASKUNMASK_ENTRY PciMsixMaskmaskEntry;NTSTATUS PciMsixMaskmaskEntry([in] PVOID Context,[in] ULONG TableEntry
)
{...}PCI_MSIX_MASKUNMASK_ENTRY PciMsixMaskunmaskEntry;NTSTATUS PciMsixMaskunmaskEntry([in] PVOID Context,[in] ULONG TableEntry
)
{...}PCI_MSIX_SET_ENTRY PciMsixSetEntry;NTSTATUS PciMsixSetEntry([in] PVOID Context,[in] ULONG TableEntry,[in] ULONG MessageNumber
)
{...}

默认情况下,中断表配置为使第一个条目的消息 ID 为零,第二个条目具有消息 ID 1,依此依此。 如果表条目数超过消息数,则向每个附加表条目分配消息 ID 为零。 (消息 ID 是描述驱动程序的消息信号中断的IO_INTERRUPT_MESSAGE_INFO结构的 MessageInfo 成员中中断项的索引。IoConnectInterruptEx 例程提供指向此结构的指针)。

IoConnectInterruptEx 

这个函数比较有意思

#define IoConnectInterruptEx WdmlibIoConnectInterruptExNTSTATUS WdmlibIoConnectInterruptEx([in, out] PIO_CONNECT_INTERRUPT_PARAMETERS Parameters
);typedef struct _IO_CONNECT_INTERRUPT_PARAMETERS {ULONG Version;union {IO_CONNECT_INTERRUPT_FULLY_SPECIFIED_PARAMETERS FullySpecified;IO_CONNECT_INTERRUPT_LINE_BASED_PARAMETERS      LineBased;IO_CONNECT_INTERRUPT_MESSAGE_BASED_PARAMETERS   MessageBased;};
} IO_CONNECT_INTERRUPT_PARAMETERS, *PIO_CONNECT_INTERRUPT_PARAMETERS;

使用 CONNECT_FULLY_SPECIFIED 的 IoConnectInterruptEx

驱动程序可以使用 ioConnectInterruptEx CONNECT_FULLY_SPECIFIED 版本为特定中断注册 InterruptService 例程。 从 Windows Vista 开始,驱动程序可以使用CONNECT_FULLY_SPECIFIED版本。 通过链接到 Iointex.lib 库,驱动程序可以使用 Windows 2000、Windows XP 和 Windows Server 2003 中的 CONNECT_FULLY_SPECIFIED 版本。 

驱动程序将 parameters->Version 的值指定为 CONNECT_FULLY_SPECIFIED,并使用 Parameters->FullySpecified 的成员来指定操作的其他参数:

Parameters->FullySpecified.PhysicalDeviceObject 为 ISR 服务的设备指定 PDO。

Parameters->FullySpecified.ServiceRoutine 指向 InterruptService 例程,而 Parameters->FullySpecified。ServiceContext 指定系统作为 ServiceContext 参数传递给 InterruptService 的值。 驱动程序可以使用此来传递上下文信息。 

驱动程序提供指向 Parameters-> FullySpecified.InterruptObject中的 PKINTERRUPT 变量的指针。 IoConnectInterruptEx 例程将此变量设置为指向中断的中断对象,该对象可在删除 ISR 时使用。

驱动程序可以选择在 Parameters->FullySpecified.SpinLock 中指定旋转锁,供系统在与 ISR 同步时使用。 大多数驱动程序只需指定 NULL ,使系统能够代表驱动程序分配旋转锁。 

驱动程序必须在 Parameters->FullySpecified 的其他成员中指定中断的关键属性。 系统在将 IRP_MN_START_DEVICE IRP 发送到驱动程序时,在 CM_PARTIAL_RESOURCE_DESCRIPTOR 结构数组中提供必要的信息。

系统为每个中断提供一个类型成员等于 CmResourceTypeInterrupt的CM_PARTIAL_RESOURCE_DESCRIPTOR结构。 对于消息信号中断,设置 Flags 成员的CM_RESOURCE_INTERRUPT_MESSAGE位;否则,会将其清除。

CM_PARTIAL_RESOURCE_DESCRIPTOR 的 u.Interrupt 成员包含基于行的中断的说明,而 u.MessageInterrupt.Translated 成员包含消息信号中断的说明:

成员基于行的中断消息信号中断

ShareVector

ShareDisposition

ShareDisposition

vector

u.Interrupt.Vector

u.MessageInterrupt.Translated.Vector

Irql

u.Interrupt.Level

u.MessageInterrupt.Translated.Level

InterruptMode

type & CM_RESOURCE_INTERRUPT_LATCHED

type & CM_RESOURCE_INTERRUPT_LATCHED

ProcessorEnableMask

u.Interrupt.Affinity

u.MessageInterrupt.Translated.Affinity

 下面是实际的代码: 

IO_CONNECT_INTERRUPT_PARAMETERS params;// deviceExtension is a pointer to the driver's device extension. 
//     deviceExtension->IntObj is a PKINTERRUPT.
// deviceInterruptService is a pointer to the driver's InterruptService routine.
// IntResource is a CM_PARTIAL_RESOURCE_DESCRIPTOR structure of either type CmResourceTypeInterrupt or CmResourceTypeMessageInterrupt.
// PhysicalDeviceObject is a pointer to the device's PDO. 
// ServiceContext is a pointer to driver-specified context for the ISR.RtlZeroMemory( &params, sizeof(IO_CONNECT_INTERRUPT_PARAMETERS) );
params.Version = CONNECT_FULLY_SPECIFIED;
params.FullySpecified.PhysicalDeviceObject = PhysicalDeviceObject;
params.FullySpecified.InterruptObject = &devExt->IntObj;
params.FullySpecified.ServiceRoutine = deviceInterruptService;
params.FullySpecified.ServiceContext = ServiceContext;
params.FullySpecified.FloatingSave = FALSE;
params.FullySpecified.SpinLock = NULL;if (IntResource->Flags & CM_RESOURCE_INTERRUPT_MESSAGE) {// The resource is for a message-signaled interrupt. Use the u.MessageInterrupt.Translated member of IntResource.params.FullySpecified.Vector = IntResource->u.MessageInterrupt.Translated.Vector;params.FullySpecified.Irql = (KIRQL)IntResource->u.MessageInterrupt.Translated.Level;params.FullySpecified.SynchronizeIrql = (KIRQL)IntResource->u.MessageInterrupt.Translated.Level;params.FullySpecified.ProcessorEnableMask = IntResource->u.MessageInterrupt.Translated.Affinity;
} else {// The resource is for a line-based interrupt. Use the u.Interrupt member of IntResource.params.FullySpecified.Vector = IntResource->u.Interrupt.Vector;params.FullySpecified.Irql = (KIRQL)IntResource->u.Interrupt.Level;params.FullySpecified.SynchronizeIrql = (KIRQL)IntResource->u.Interrupt.Level;params.FullySpecified.ProcessorEnableMask = IntResource->u.Interrupt.Affinity;
}params.FullySpecified.InterruptMode = (IntResource->Flags & CM_RESOURCE_INTERRUPT_LATCHED ? Latched : LevelSensitive);
params.FullySpecified.ShareVector = (BOOLEAN)(IntResource->ShareDisposition == CmResourceShareShared);status = IoConnectInterruptEx(&params);if (!NT_SUCCESS(status)) {...
}

这篇关于windows驱动开发-PCI和中断(三)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/991242

相关文章

一文详解Python如何开发游戏

《一文详解Python如何开发游戏》Python是一种非常流行的编程语言,也可以用来开发游戏模组,:本文主要介绍Python如何开发游戏的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录一、python简介二、Python 开发 2D 游戏的优劣势优势缺点三、Python 开发 3D

Linux挂载linux/Windows共享目录实现方式

《Linux挂载linux/Windows共享目录实现方式》:本文主要介绍Linux挂载linux/Windows共享目录实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录文件共享协议linux环境作为服务端(NFS)在服务器端安装 NFS创建要共享的目录修改 NFS 配

基于Python开发Windows自动更新控制工具

《基于Python开发Windows自动更新控制工具》在当今数字化时代,操作系统更新已成为计算机维护的重要组成部分,本文介绍一款基于Python和PyQt5的Windows自动更新控制工具,有需要的可... 目录设计原理与技术实现系统架构概述数学建模工具界面完整代码实现技术深度分析多层级控制理论服务层控制注

Java中的分布式系统开发基于 Zookeeper 与 Dubbo 的应用案例解析

《Java中的分布式系统开发基于Zookeeper与Dubbo的应用案例解析》本文将通过实际案例,带你走进基于Zookeeper与Dubbo的分布式系统开发,本文通过实例代码给大家介绍的非常详... 目录Java 中的分布式系统开发基于 Zookeeper 与 Dubbo 的应用案例一、分布式系统中的挑战二

Oracle数据库在windows系统上重启步骤

《Oracle数据库在windows系统上重启步骤》有时候在服务中重启了oracle之后,数据库并不能正常访问,下面:本文主要介绍Oracle数据库在windows系统上重启的相关资料,文中通过代... oracle数据库在Windows上重启的方法我这里是使用oracle自带的sqlplus工具实现的方

基于Go语言开发一个 IP 归属地查询接口工具

《基于Go语言开发一个IP归属地查询接口工具》在日常开发中,IP地址归属地查询是一个常见需求,本文将带大家使用Go语言快速开发一个IP归属地查询接口服务,有需要的小伙伴可以了解下... 目录功能目标技术栈项目结构核心代码(main.go)使用方法扩展功能总结在日常开发中,IP 地址归属地查询是一个常见需求:

基于 Cursor 开发 Spring Boot 项目详细攻略

《基于Cursor开发SpringBoot项目详细攻略》Cursor是集成GPT4、Claude3.5等LLM的VSCode类AI编程工具,支持SpringBoot项目开发全流程,涵盖环境配... 目录cursor是什么?基于 Cursor 开发 Spring Boot 项目完整指南1. 环境准备2. 创建

SpringBoot 多环境开发实战(从配置、管理与控制)

《SpringBoot多环境开发实战(从配置、管理与控制)》本文详解SpringBoot多环境配置,涵盖单文件YAML、多文件模式、MavenProfile分组及激活策略,通过优先级控制灵活切换环境... 目录一、多环境开发基础(单文件 YAML 版)(一)配置原理与优势(二)实操示例二、多环境开发多文件版

使用docker搭建嵌入式Linux开发环境

《使用docker搭建嵌入式Linux开发环境》本文主要介绍了使用docker搭建嵌入式Linux开发环境,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录1、前言2、安装docker3、编写容器管理脚本4、创建容器1、前言在日常开发全志、rk等不同

Python实战之SEO优化自动化工具开发指南

《Python实战之SEO优化自动化工具开发指南》在数字化营销时代,搜索引擎优化(SEO)已成为网站获取流量的重要手段,本文将带您使用Python开发一套完整的SEO自动化工具,需要的可以了解下... 目录前言项目概述技术栈选择核心模块实现1. 关键词研究模块2. 网站技术seo检测模块3. 内容优化分析模