一个网站的优化怎么做木马科技网站建设

张小明 2026/1/8 21:18:02
一个网站的优化怎么做,木马科技网站建设,wordpress阿里云主机,公司网站制作效果怎么样一、中断的引入类比查询方式#xff1a;CPU轮询设备状态#xff0c;简单但效率低#xff0c;CPU资源浪费休眠-唤醒#xff1a;进程阻塞等待#xff0c;不浪费CPU资源#xff0c;但无法做其他工作poll方式#xff1a;定时检查#xff0c;仍有时间浪费异步通知#xff1…一、中断的引入类比查询方式CPU轮询设备状态简单但效率低CPU资源浪费休眠-唤醒进程阻塞等待不浪费CPU资源但无法做其他工作poll方式定时检查仍有时间浪费异步通知中断机制双方互不耽误实时响应二、中断的整体流程1.初始化阶段a、设置中断源配置设备使其能产生中断b、设置中断控制器屏蔽/使能特定中断、设置中断优先级c、设置CPU总开关使能全局中断2.正常执行阶段CPU执行主程序每执行完一条指令都会检查有无中断/异常产生3.中断响应阶段硬件自动a、设备产生中断信号b、中断控制器接收并判断优先级c、中断控制器通知CPUd、CPU检测到中断暂停当前程序e、根据异常类型跳转到对应的异常向量地址地址上的内容只是一条跳转指令跳去执行某个函数4.中断处理阶段软件实现a、保存现场保护寄存器等上下文信息b、处理异常识别具体中断源、调用对应的中断处理函数c、恢复现场恢复寄存器返回被中断的程序三、异常向量表异常有多种中断也属于一种异常1.基本概念一段固定的代码区域包含各种异常的处理入口每种异常对应一个固定的偏移地址ARM9芯片中断向量地址固定为0x18当发生中断时CPU就强制跳去执行0x18处的指令2.典型向量表示例_start: b reset // 复位偏移0x00 ldr pc, _undefined_instruction // 未定义指令偏移0x04 ldr pc, _software_interrupt // 软中断偏移0x08 ldr pc, _prefetch_abort // 预取中止偏移0x0C ldr pc, _data_abort // 数据中止偏移0x10 ldr pc, _not_used // 保留偏移0x14 ldr pc, _irq // IRQ中断偏移0x18 ★ ldr pc, _fiq // FIQ快中断偏移0x1C3.向量表位置默认从0地址开始可通过vector base寄存器重定位如设置到DDR的0x80000000关键各异常向量的偏移地址固定不变复位0x00中断0x18四、中断演进1、硬件中断的两大核心原则不能嵌套防止无限递归导致内核栈溢出。因此 Linux 内核采用关中断处理硬件 IRQ的方式处理完当前中断后才重新开启中断越快越好中断处理期间无法调度进程耗时过长会导致系统卡顿。但有时硬件 IRQ 要做的事情本身就很多因此需要中断分层2、Linux 的中断扩展硬件中断 软件中断硬件中断Hard IRQa、由外设触发如 GPIO、网卡、USB。b、每个硬件 IRQ 对应一个处理函数handler。软件中断Soft IRQa、由软件触发用于推迟处理不紧急但需要较高性能的任务。b、通过 raise_softirq() 设置 softirq 标志在处理完硬件中断后才执行。c、软中断包括如HI_SOFTIRQ、TIMER_SOFTIRQ、NET_TX_SOFTIRQ、TASKLET_SOFTIRQ等。用途实现网络收发、tasklet 等机制。3、中断上半部 / 下半部机制为解决“中断任务太多导致系统卡顿”的问题Linux 将中断分为两部分上半部top halfa、由硬件中断直接触发b、在关中断状态执行c、应尽快处理d、只能做最关键、最短的工作如读取寄存器清 pending下半部bottom halfa、在开中断状态运行b、由内核调度c、做耗时的任务如延时、复杂处理围绕如何实现“下半部”发展出了两种主要技术其特点和适用场景对比如下特性Tasklet (小任务)工作队列 (Work Queue)本质基于软件中断(TASKLET_SOFTIRQ)实现由内核线程 (kworker)执行执行上下文中断上下文不能休眠进程上下文可以休眠执行特点多个上半部可能对应一次下半部执行多对一真正的异步执行适用场景耗时较短、操作简单的任务非常耗时或复杂的任务需要睡眠、锁等操作对系统影响若耗时过长仍会阻塞其他软中断和进程将负载转移到线程不影响其他中断响应4、现代方案Threaded IRQ线程化中断a、工作方式驱动程序可使用request_threaded_irq函数直接提供一个线程处理函数 (thread_fn)。内核会为每个中断单独创建一个内核线程来处理它。b、核心优势负载均衡在SMP多核系统中不同中断的线程可以分配到不同CPU上执行充分利用多核能力。开发简化相比传统的工作队列API更简洁无需手动管理 work 和 workqueue。实时性赋予了中断处理明确的调度策略和优先级对实时系统更友好。五、中断子系统的重要结构体1、irq_desc--- 核心管理层handle_irq: 最重要的成员之一指向该中断的流控处理函数。根据中断触发类型电平/边沿内核会设置不同的函数如handle_level_irq、handle_edge_irq这些函数负责调用驱动注册的处理函数。action: 链表头共享中断时会有多个irqaction节点非共享时只有一个。irq_data: 直接内嵌在irq_desc中不是指针体现了硬件数据与软件描述符的紧密关联。2、irqaction--- 驱动接口层handler/thread_fn: 体现了中断处理的两种模式。传统模式只用handler线程化中断模式可只用thread_fn或两者都用handler作为上半部。dev_id:共享中断的关键用于在卸载中断时准确识别要删除哪个处理函数也作为参数传递给处理函数。3、irq_data--- 硬件映射层irq/hwirq: 清晰地区分了软件视角和硬件视角的中断号。domain: 指向负责hwirq→irq映射的域在多级中断控制器系统中尤为重要。4、irq_domain--- 硬件抽象层ops-xlate: 从设备树interrupts属性解析出硬件中断号和flags。ops-map: 建立硬件中断号到软件中断号的映射。linear_revmap[]: 常用的优化提供从hwirq快速查找irq的反向映射。5、irq_chip--- 硬件操作层irq_ack: 有些控制器需要在处理开始时就应答中断。irq_mask/unmask: 与irq_enable/disable的区别在于mask/unmask通常指硬件层面的屏蔽而enable/disable可能涉及软件状态管理。扩展内核在逻辑上有一个irq_desc结构体数组但在物理实现上它可能是一个数组也可能是一个基数树Radix Tree。这取决于内核的配置。a、逻辑上的“数组”无论底层如何实现从驱动开发者和大部分内核代码的角度看访问中断描述符的方式就像访问一个数组通过一个整数索引即虚拟virq来获取对应的struct irq_desc *。这是通过irq_to_desc(virq)或irq_data_to_desc()等函数完成的。b、物理实现数组还是基数树这是由内核编译配置CONFIG_SPARSE_IRQ决定的。下图清晰地展示了两者的区别与选择逻辑传统方式静态数组早期内核和许多嵌入式系统采用的方式。在编译时通过NR_IRQS定义一个固定的数组大小例如struct irq_descirq_desc[NR_IRQS]。缺点如果系统实际使用的中断号很稀疏比如只用了第5、第1000号中断中间大量的数组项内存就被浪费了。NR_IRQS必须设置为可能的最大值这会增加内核的内存占用。现代方式基数树这是当前主流桌面、服务器和复杂嵌入式系统如ARMv8、多核SoC的默认配置。内核使用基数树来动态存储irq_desc结构体。只有当某个中断号第一次被分配使用时才为其分配并插入一个irq_desc对象。优点节省内存只为实际使用的中断分配描述符。支持更大的中断号空间不受NR_IRQS静态限制可以轻松支持成千上万个中断这在大型PCIe系统中很常见。支持中断热插拔可以动态地分配和释放中断描述符适合设备热插拔场景。六、中断的使用第一步在设备树中指定中断这是描述硬件连接的关键步骤需要准确反映中断信号的物理路径。1、中断控制器节点a、必须属性interrupt-controller和#interrupt-cells。b、#interrupt-cells的含义指定引用此控制器时需要几个cell来描述一个中断。1通常只需要一个cell指定中断编号。2第一个cell指定中断编号第二个cell指定触发类型如上升沿、高电平等。层级关系下级中断控制器如GPIO模块需要通过interrupt-parent属性指向其父控制器如GIC并使用interrupts属性说明自己使用了父控制器的哪个些中断线。2、设备节点使用中断在外设节点中需要说明它连接到哪个中断控制器的哪个引脚a、interrupt-parent 控制器标签;指向所使用的中断控制器节点。b、interrupts ... ;描述具体中断。cell的数量和含义由父控制器的#interrupt-cells决定。示例interrupts 5 IRQ_TYPE_EDGE_RISING;表示使用父控制器的第5号中断上升沿触发。简化写法可使用interrupts-extended属性同时指定父控制器和中断信息。第二步在驱动代码中获得中断号这是软件读取硬件配置的关键方法取决于设备节点在内核中被转换为何种设备类型。这直接对应了上图中不同的获取路径。设备节点类型转换后的内核结构体获取中断号的典型方法代码示例/说明Platform设备platform_deviceplatform_get_resource适用于大多数直接挂在内存总线上的设备。I2C设备i2c_client从i2c_client-irq直接读取I2C总线驱动在创建i2c_client时已解析并填充了中断号。SPI设备spi_device从spi_device-irq直接读取SPI总线驱动在创建spi_device时已解析并填充了中断号。GPIO中断无统一设备结构gpio_to_irq或gpiod_to_irq最常用。先通过of_get_gpio获得GPIO号再转换为中断号。其他/特殊情况无对应转换of_irq_get一个通用的“保底”函数可直接解析设备树节点的中断属性。七、中断的硬件框架1、中断路径上的三个核心部件a、中断源多种外设可触发中断如GPIO、定时器、UART、DMA等。每个中断源有独立的寄存器用于配置使能、状态、触发类型等。b、中断控制器汇总所有中断信号进行优先级仲裁并向CPU发出中断请求。不同架构/芯片使用不同的中断控制器在ARM体系结构中最常用的中断控制器主要有两种它们分别面向不同系列的ARM处理器和不同的应用场景1、NVICNested Vectored Interrupt Controller适用处理器系列Cortex-M系列如Cortex-M0/M3/M4/M7/M23/M33等主要特点向量化中断每个中断有固定入口地址响应速度快。硬件优先级支持硬件优先级判断和嵌套。寄存器统一所有Cortex-M处理器使用相同的NVIC编程模型。集成度高通常与处理器内核紧密结合。2、GICGeneric Interrupt Controller适用处理器系列Cortex-A系列如Cortex-A7/A8/A9/A53/A55/A76/A78等主要特点多核支持能将中断路由到不同CPU核。高度可配置支持软件中断、虚拟化中断。安全扩展支持TrustZone区分安全/非安全中断。可扩展性强支持大量中断源SPI、PPI、SGI。对比总结特性NVICGIC目标平台微控制器MCU应用处理器MPU/SoC复杂度简单、轻量复杂、功能丰富多核支持无有虚拟化支持无有GICv2以上典型芯片STM32F103/407, GD32, ESP32树莓派BCM2711, IMX6ULL, STM32MP157发展趋势随着物联网和边缘计算的发展现在也有一些混合架构的芯片如Cortex-M Cortex-A异构系统如STM32MP1多核Cortex-M如双核Cortex-M33在这样的系统中通常会同时包含NVIC用于Cortex-M和GIC用于Cortex-A两者协同工作。c、CPU每执行完一条指令会检查是否有中断请求。通过内部寄存器控制中断总开关如Cortex-M3/M4PRIMASK、FAULTMASK、BASEPRICortex-A系列CPSR中的I位接下来我们主要了解GIC八、GIC相关介绍1、GIC的两种主要功能模块a、Distributor分发器连接系统中所有中断源。配置中断属性优先级、目标CPU、触发方式、使能状态等。仲裁并分发中断到对应的CPU接口。b、CPU InterfaceCPU接口单元每个CPU核独有一个。接收分发器发来的中断并向CPU核发出中断信号。提供中断确认、结束中断等控制功能。2、GIC 中断类型类型中断号范围说明SGI软件触发中断0 ~ 15由软件写入ICDSGIR生成用于核间通信。PPI私有外设中断16 ~ 31各CPU核私有外设产生如核内定时器。SPI共享外设中断32 ~ 1020外设产生可路由到多个CPU核。中断ID 1020~1023保留1023为伪中断IDspurious interrupt。3、中断触发方式边沿触发检测到上升沿即触发状态保持到清除。电平触发输入为高电平时触发。4、中断状态状态说明Inactive未触发。Pending已触发等待CPU处理。Active正在被CPU处理。Active and Pending正在处理中又收到同一中断。九、GIC驱动对中断的处理流程1、一级中断控制器处理流程示例UART中断处理在内核中中断描述符irq_desc的分配有两种方式一次性分配所有irq_desc按需分配主流假设系统中GIC可以处理16~1019号硬件中断hwirq其中0~15号中断保留用于处理器间通信特殊用途UART模块发出的中断连接到GIC的第32号硬件中断hwirq32内核为该中断分配的虚拟中断号virq为16在GIC的中断域irq_domain中会记录映射关系(32, 16)中断处理流程a、驱动程序注册中断时使用虚拟中断号request_irq(16, handle_irq)b、当UART中断发生时CPU从GIC寄存器中读取到发生了32号硬件中断通过GIC的中断域映射查找到对应的虚拟中断号为16调用irq_desc[16]中的处理函数handleAGIC驱动选择并设置为内核提供的标准函数handleA函数负责调用该中断action链表中所有用户注册的中断服务程序2、级联中断控制器处理流程示例GPIO按键中断处理在实际系统中可能存在多级中断控制器。以下以GPIO控制器为例假设系统中GPIO模块有4个引脚引脚0~3每个引脚都能产生中断所有GPIO引脚的中断信号都汇集到GIC的第33号硬件中断hwirq33GPIO模块本身也作为一个中断控制器管理其4个引脚的中断初始化过程内核为GPIO的4个引脚一次性分配4个中断描述符假设分配的虚拟中断号为100~103在GPIO的中断域中建立映射(0, 100)- GPIO引脚0对应虚拟中断号100(1, 101)- GPIO引脚1对应虚拟中断号101(2, 102)- GPIO引脚2对应虚拟中断号102(3, 103)- GPIO引脚3对应虚拟中断号103中断处理流程以按键连接到GPIO引脚2为例a、驱动程序注册中断时使用GPIO引脚对应的虚拟中断号request_irq(102, ...)b、当按键被按下时首先CPU从GIC寄存器中读取到发生了33号硬件中断通过GIC的中断域映射查找到对应的虚拟中断号为16调用irq_desc[17]中的处理函数handleB这是GPIO控制器驱动提供的的中断处理函数handleB函数执行以下操作读取GPIO控制器的寄存器确定是哪个引脚触发了中断假设是引脚2通过GPIO的中断域映射查找到引脚2对应的虚拟中断号为102调用irq_desc[102]中的处理函数handleAhandleA函数负责调用该中断action链表中所有用户注册的中断服务程序handleA由GPIO驱动设置但函数本身是内核通用中断处理函数3、需要注意的特殊细节对于一级中断控制器如UARTirq_desc[16]中的handleA是由GIC驱动设置的内核通用处理函数如handle_fasteoi_irq、handle_level_irqGIC驱动还设置了对应的irq_chip操作集gic_chip对于级联中断控制器如GPIOirq_desc[17]中的handleB初始由GIC驱动选择合适的标准处理函数提供后被次级控制器驱动替换为自己的处理函数由GPIO控制器驱动实现如gpio_irq_handler但 irq_desc[102] 中的handleA是GPIO驱动设置的内核通用函数如handle_level_irq 、handle_edge_irq4、GIC中断号如何对应到 Linux 的“虚拟中断号”这是软件决定的Linux 内核层。在 Linux 里概念由谁决定GIC 中断 ID硬件中断号SoC 硬件决定Linux IRQ number虚拟中断号内核根据 irq_domain 分配Linux 为不同硬件控制器建立了irq_domain用来把硬件中断号虚拟中断号进行映射。典型流程设备树DTS声明 GPIO 控制器有一个 SPI 中断比如 32内核建立映射hwirq (32) → virq (如 150)Linux 在 GIC的irq_domain中记录映射的信息十、两类中断控制器1、两类中断控制器的区别a、链式中断控制器Chained Interrupt Controller工作原理子中断控制器的所有中断引脚都映射到GIC的同一个中断号上。举例比如一个GPIO控制器有4个引脚它们的中断都触发GIC的第33号中断。中断识别当GIC的33号中断被触发时处理器需要读取子中断控制器的寄存器来判断具体是哪一个引脚产生的中断。特点需要“二次判断”增加了处理开销。b、层级中断控制器Hierarchy Interrupt Controller工作原理子中断控制器的每个中断引脚都直接对应GIC的一个独立中断号。举例GPIO的4个引脚分别对应GIC的100、101、102、103号中断。中断识别当中断发生时GIC直接知道是哪一个中断号无需再查询子控制器。特点结构清晰处理效率高。2、链式中断控制器的处理流程以GPIO为例假设GPIO控制器的4个引脚都连接到GIC的33号中断中断触发按下某个GPIO引脚比如2号引脚触发中断。GIC响应GIC检测到33号中断通过其GIC的irq_domain映射到虚拟中断号virq17。第一级处理调用irq_desc[17].handle_irq由GPIO驱动实现的handleB。先mask/ack GIC的33号中断通过GIC提供的irq_dataA。读取GPIO寄存器确定是2号引脚中断。通过GPIO的irq_domain映射到virq 102。第二级处理调用irq_desc[102].handle_irq由GPIO驱动设置的handleC。mask/ack GPIO的2号中断通过GPIO自己的irq_data。执行用户注册的中断处理函数action链表。unmask中断。返回第一级unmask GIC的33号中断。核心特点需要两次处理第一次在GIC层第二次在GPIO层中间需要读取子控制器寄存器进行分辨。3、层级中断控制器的处理流程假设GPIO的4个引脚分别对应GIC的100~103号中断中断触发按下2号引脚触发GIC的102号中断。GIC映射GIC通过其irq_domain直接映射到virq 236。直接处理调用irq_desc[236].handle_irqGIC设置的handleA。mask/ack中断调用GPIO驱动提供的irq_dataB它会再调用父级GIC的irq_dataA。执行用户注册的中断处理函数action链表。unmask中断同样通过irq_dataB调用父级的unmask函数。无需查询子控制器整个过程不需要读取GPIO寄存器来判断中断源。核心特点中断号一一对应处理流程直接而高效无需“二次分辨”。4、两类控制器对比项目链式Chained层级Hierarchy中断映射多个子中断映射到同一个GIC中断号子中断与GIC中断号一一对应中断识别需要读取子控制器寄存器直接通过GIC中断号识别处理流程两级处理效率较低一级处理效率高适用场景中断引脚多但GIC中断资源有限中断资源充足结构清晰代码复杂度较高需要额外的分辨逻辑较低结构直接十一、一级中断控制器驱动程序框架GIC1、GIC初始化流程start_kernel (init/main.c) │ // Linux内核启动的主函数,初始化各个子系统 │ └─ init_IRQ (arch/arm/kernel/irq.c) │ // 体系结构相关的中断初始化入口 │ // ARM架构在这里初始化中断控制器 │ └─ irqchip_init (drivers/irqchip/irqchip.c) │ // 通用中断控制器初始化框架 │ // 负责调用所有注册的irqchip驱动初始化函数 │ └─ of_irq_init (drivers/of/irq.c) │ // 基于设备树(Device Tree)的中断控制器初始化 │ // 按照中断控制器的层级关系依次初始化 │ ├─ 遍历__irqchip_of_table数组 │ // __irqchip_of_table是链接脚本定义的特殊段 │ // 包含所有通过IRQCHIP_DECLARE宏注册的中断控制器信息 │ // 每个条目包含: compatible字符串和初始化函数指针 │ // 例如: {arm,gic-400, gic_of_init} │ ├─ 匹配设备树中的compatible属性 │ // 在设备树中查找interrupt-controller节点 │ // 比对节点的compatible属性与__irqchip_of_table中的字符串 │ // 优先初始化没有interrupt-parent的根中断控制器(如GIC) │ // 然后按层级初始化子中断控制器(如GPIO中断控制器) │ └─ 调用匹配项的初始化函数 gic_of_init() │ // GIC驱动的初始化入口函数 │ // 完成GIC硬件的配置和软件数据结构的建立 │ ├─ 解析设备树获取GIC基地址 │ // 从设备树节点的reg属性读取寄存器基地址 │ // GIC v2有两个地址: │ // - GICD_BASE: Distributor(分发器)基地址 │ // - GICC_BASE: CPU Interface(CPU接口)基地址 │ // 通过ioremap()映射到内核虚拟地址空间 │ ├─ 初始化gic_chip_data结构体 │ // gic_chip_data是GIC驱动的核心数据结构 │ // 保存GIC的配置信息: │ // - dist_base: Distributor寄存器基地址 │ // - cpu_base: CPU Interface寄存器基地址 │ // - domain: 指向irq_domain的指针 │ // - gic_irqs: 支持的中断数量 │ // 配置GIC硬件寄存器: │ // - 禁用所有中断(GICD_ICENABLERn) │ // - 设置中断优先级(GICD_IPRIORITYRn) │ // - 配置中断目标CPU(GICD_ITARGETSRn) │ // - 启用GIC Distributor(GICD_CTLR) │ ├─ 设置irq_chip(中断控制操作函数) │ // irq_chip提供中断控制器的底层操作接口 │ // 定义了对硬件中断的操作方法: │ // - irq_mask: 屏蔽中断(写GICD_ICENABLERn) │ // - irq_unmask: 使能中断(写GICD_ISENABLERn) │ // - irq_ack: 应答中断(读GICC_IAR) │ // - irq_eoi: 结束中断(写GICC_EOIR) │ // - irq_set_type: 设置触发类型(边沿/电平) │ // - irq_set_affinity: 设置CPU亲和性 │ // 这些操作函数会被通用中断层调用 │ ├─ 创建irq_domain │ // irq_domain是hwirq到virq映射的管理器 │ // 调用irq_domain_add_linear()或irq_domain_add_hierarchy() │ // 参数包括: │ // - device_node: 设备树节点 │ // - size: 支持的hwirq数量(如160个) │ // - ops: irq_domain_ops操作集 │ // * xlate/translate: 解析设备树中断描述符 │ // * map/alloc: 创建hwirq到virq的映射 │ // - host_data: 指向gic_chip_data │ // irq_domain负责: │ // - 分配virq编号 │ // - 建立hwirq-virq的反向映射表(revmap) │ // - 为每个virq创建和配置irq_desc │ └─ 设置gic_handle_irq函数指针 // gic_handle_irq是全局函数指针(定义在arch/arm/kernel/irq.c) // 调用set_handle_irq(gic_handle_irq)设置为GIC的中断处理入口 // 当硬件中断发生时,CPU异常向量会调用这个函数: // 1. 读取GICC_IAR寄存器获取hwirq // 2. 通过irq_domain将hwirq转换为virq // 3. 调用handle_domain_irq(domain, hwirq, regs) // 4. 最终执行irq_desc[virq]-handle_irq() // 这是硬件中断进入Linux中断处理框架的关键桥梁 // 初始化完成后: // - GIC硬件已配置并启用 // - irq_domain已创建,可以进行hwirq到virq的映射 // - gic_handle_irq已设置,可以处理硬件中断 // - 设备驱动可以通过设备树申请和使用中断2、中断申请流程a、设备树中申请使用中断b、解析中断platform_device创建 └─ of_device_alloc (drivers/of/platform.c) │ // 从设备树节点创建platform_device,并自动解析中断资源 │ ├─ 分配platform_device │ // 为设备分配内存空间 │ ├─ of_irq_count() - 计算设备的中断数量 │ // 通过解析设备树节点的interrupts或interrupts-extended属性 │ // 确定该设备有多少个中断源,返回中断数量 │ └─ of_irq_to_resource_table() - 构造中断资源 │ // 将设备树中的中断信息转换为platform_device的resource数组 │ // 每个中断对应一个IORESOURCE_IRQ类型的resource │ └─ of_irq_to_resource() │ // 处理单个中断,填充resource结构体 │ └─ irq_of_parse_and_map() - 核心函数 │ // 解析设备树中断描述符并映射为Linux虚拟中断号(virq) │ ├─ of_irq_parse_one() - 解析设备树中断信息 │ │ // 解析interrupts属性和interrupt-parent │ │ // 通过interrupt-parent找到对应的中断控制器节点 │ │ // 根据中断控制器的#interrupt-cells属性解析中断描述符 │ │ │ └─ 获取hwirq和flags │ // hwirq: 硬件中断号(如GIC中的中断ID) │ // flags: 中断触发类型(边沿/电平,上升/下降等) │ └─ irq_create_of_mapping() │ // 创建或查找hwirq到virq的映射关系 │ └─ irq_create_fwspec_mapping() │ // 固件规范映射的通用接口 │ ├─ irq_domain_translate() - 翻译hwirq和type │ // 调用irq_domain的translate回调函数 │ // 将设备树中的中断描述符转换为标准的hwirq和type │ // 例如: GIC中将0 33 4转换为hwirq33, typeIRQ_TYPE_LEVEL_HIGH │ ├─ irq_find_mapping() - 查找是否已映射 │ // 在irq_domain的revmap(反向映射表)中查找 │ // 如果该hwirq已经分配了virq,直接返回现有的virq │ // 避免重复映射同一个硬件中断 │ └─ 如果未映射则创建: │ // 两种映射方式,取决于irq_domain的类型 │ ├─ [层级domain] irq_domain_alloc_irqs() │ │ // 用于层级中断域(Hierarchical IRQ Domain) │ │ // 适用于GIC v3、MSI等支持层级的中断控制器 │ │ // 可以同时分配父域和子域的irq_data │ │ │ └─ 调用ops-alloc设置irq_desc │ // 调用irq_domain_ops-alloc()回调 │ // 分配virq,创建irq_desc,设置irq_data │ // 配置irq_chip(提供mask/unmask/ack等操作) │ // 设置handle_irq(如handle_fasteoi_irq) │ └─ [非层级(链式)domain] irq_create_mapping() │ // 用于传统的线性映射(Linear Mapping) │ // 适用于GIC v2等非层级中断控制器 │ // 先分配一个未使用的virq │ └─ 调用ops-map设置irq_desc // 调用irq_domain_ops-map()回调 // 将hwirq与virq关联,填充irq_desc // 设置irq_chip和handle_irq // 更新revmap反向映射表 // 此时platform_device-resource[]中已包含virq // 驱动通过platform_get_irq()获取virq // 再调用request_irq(virq, handler, flags, name, dev)注册中断处理函数3、中断处理流程硬件中断发生 │ // 外设产生中断信号,通过中断线传递到GIC │ // GIC接收到中断后,根据优先级和CPU亲和性选择目标CPU │ // 向目标CPU发送IRQ或FIQ信号 │ └─ CPU跳转到异常向量表 │ // CPU检测到IRQ信号,硬件自动完成: │ // 1. 保存当前CPSR到SPSR_irq │ // 2. 切换到IRQ模式,禁用中断 │ // 3. 保存返回地址到LR_irq │ // 4. PC跳转到异常向量表的IRQ入口(0xFFFF0018或0x00000018) │ // 异常向量表中的指令跳转到__irq_svc或__irq_usr │ // 这些汇编代码保存现场,然后调用C函数处理中断 │ └─ gic_handle_irq() - GIC驱动设置的函数指针 │ // 实际指向gic_handle_irq函数(在GIC初始化时设置) │ // 这是GIC驱动提供的中断处理入口 │ // 参数regs包含中断发生时的CPU寄存器状态 │ ├─ 读取GIC寄存器获取hwirq │ // 读取GICC_IAR(Interrupt Acknowledge Register) │ // IAR寄存器返回当前待处理的最高优先级中断号 │ // 读操作同时完成中断应答(acknowledge),将中断状态从pending变为active │ // 返回值格式: [12:10]为CPU ID, [9:0]为interrupt ID │ // 提取interrupt ID作为hwirq(硬件中断号) │ // 特殊值1023表示spurious interrupt(伪中断,无有效中断) │ ├─ 通过irq_domain将hwirq转换为virq │ // 调用handle_domain_irq(gic_data.domain, hwirq, regs) │ // 或直接调用irq_find_mapping(domain, hwirq) │ // 在irq_domain的revmap(反向映射表)中查找hwirq对应的virq │ // revmap可以是: │ // - 线性数组: virq domain-linear_revmap[hwirq] │ // - 基数树: virq radix_tree_lookup(domain-revmap_tree, hwirq) │ // 如果未找到映射,说明该中断未被任何驱动申请,直接返回 │ └─ 调用irq_desc[virq].handle_irq() │ // irq_desc是中断描述符,包含中断的所有管理信息 │ // handle_irq是高层中断处理函数(flow handler) │ // 根据中断类型,在irq_domain映射时已设置为: │ // - handle_fasteoi_irq: 适用于电平触发中断(如GIC的SPI) │ // - handle_percpu_devid_irq: 适用于per-CPU中断(如GIC的PPI) │ // - handle_edge_irq: 适用于边沿触发中断 │ // - handle_level_irq: 传统电平触发处理 │ // 这些函数处理中断的mask/unmask/ack/eoi时序 │ ├─ [单级中断] handleA() - 直接调用action链表 │ │ // 对于直连GIC的外设中断(如UART、Timer) │ │ // 以handle_fasteoi_irq为例: │ │ // 1. 检查中断状态(IRQD_IRQ_DISABLED/IRQD_IRQ_INPROGRESS) │ │ // 2. 设置IRQD_IRQ_INPROGRESS标志,防止重入 │ │ // 3. 调用irq_chip-irq_ack()应答中断(如果需要) │ │ // 4. 遍历action链表,依次调用每个handler │ │ │ └─ 遍历irq_desc[virq].action链表 │ │ // action是通过request_irq()注册的中断处理函数链表 │ │ // 一个中断可以被多个驱动共享(IRQF_SHARED标志) │ │ // 结构体irqaction包含: │ │ // - handler: 用户注册的中断处理函数 │ │ // - thread_fn: 线程化中断处理函数(如果有) │ │ // - dev_id: 设备标识,用于区分共享中断 │ │ // - flags: 中断标志(IRQF_SHARED/IRQF_ONESHOT等) │ │ │ ├─ 调用action-handler(irq, dev_id) │ │ // 执行驱动注册的中断服务程序(ISR) │ │ // 返回值IRQ_HANDLED表示中断已处理 │ │ // 返回值IRQ_NONE表示不是该设备的中断(共享中断场景) │ │ // ISR应该快速完成,耗时操作应放到线程化中断或工作队列 │ │ │ └─ 如果有thread_fn,唤醒中断线程 │ // 线程化中断(Threaded IRQ)机制: │ // - handler在硬中断上下文快速执行,返回IRQ_WAKE_THREAD │ // - 唤醒对应的内核线程执行thread_fn │ // - thread_fn可以休眠,适合耗时的I/O操作 │ // 调用irq_chip-irq_eoi()结束中断(写GICC_EOIR) │ └─ [多级中断] handleB() │ // 对于级联中断控制器(如GPIO控制器连接到GIC) │ // GPIO控制器的中断线连到GIC的某个SPI │ // 当GPIO引脚产生中断,先触发GIC的SPI中断 │ // 以handle_simple_irq为例(GPIO控制器常用): │ // 1. 不调用irq_ack(由父中断处理) │ // 2. 直接进入中断处理逻辑 │ // 3. 不调用irq_eoi(由父中断处理) │ ├─ 读取GPIO等控制器寄存器 │ // GPIO中断处理函数(通过request_irq注册到GIC的SPI) │ // 读取GPIO控制器的中断状态寄存器 │ // 例如读取GPIO_INT_STATUS确定哪些引脚产生了中断 │ // 可能有多个引脚同时产生中断(位掩码形式) │ ├─ 确定具体引脚的hwirq │ // 遍历中断状态寄存器的每个置位bit │ // 每个bit对应一个GPIO引脚编号 │ // 引脚编号就是GPIO domain中的hwirq │ // 例如: GPIO2的第5号引脚,hwirq可能是(2*32 5) │ ├─ 通过GPIO domain转换为virq │ // GPIO控制器在初始化时创建了自己的irq_domain │ // 调用irq_find_mapping(gpio_domain, gpio_hwirq) │ // 查找该GPIO引脚对应的virq │ // 这个virq是驱动通过gpio_to_irq()或设备树申请时分配的 │ └─ 调用irq_desc[virq].handle_irq() │ // 递归调用,处理GPIO引脚的中断 │ // 这里的handle_irq通常是handle_edge_irq或handle_level_irq │ // 因为GPIO引脚支持灵活的触发类型配置 │ └─ 调用用户注册的中断处理函数 │ // 遍历该virq的action链表 │ // 执行驱动通过request_irq()注册的处理函数 │ // 例如按键驱动、触摸屏驱动的中断处理函数 │ ├─ action-handler(virq, dev_id) │ // 用户的中断服务程序 │ // 处理具体的设备事件(如读取按键状态) │ └─ 清除GPIO中断标志 // 写GPIO控制器的中断清除寄存器 // 避免中断重复触发 // 对于电平触发,还需要清除外设的中断源 // 中断处理完成后,返回路径: // 1. handleB()返回到handleA() // 2. handleA()调用irq_chip-irq_eoi()结束GIC中断(写GICC_EOIR) // 3. gic_handle_irq()返回到汇编异常处理代码 // 4. 恢复现场,返回被中断的程序继续执行十二、级联中断控制器驱动程序框架如GPIO中断控制器1、链式中断控制器a、设备树配置/* 父中断控制器 (GIC) */ gic: interrupt-controller8000000 { compatible arm,gic-400; #interrupt-cells 3; interrupt-controller; reg 0x8000000 0x1000, 0x8010000 0x2000; }; /* 链式中断控制器 */ xxx_intc: interrupt-controller1000000 { compatible xxx,intc; reg 0x1000000 0x1000; /* 声明为中断控制器 */ interrupt-controller; #interrupt-cells 2; /* hwirq flags */ /* 连接到GIC的140号SPI中断 */ interrupt-parent gic; interrupts GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH; }; /* 使用此链式中断控制器的设备 */ my_device: device2000000 { compatible xxx,device; reg 0x2000000 0x1000; /* 使用xxx_intc的1号子中断 */ interrupt-parent xxx_intc; interrupts 1 IRQ_TYPE_EDGE_RISING; };b、驱动框架/* * 第一部分IRQ Chip 硬件操作抽象 * 作用: 定义中断控制器的硬件操作接口 * */ /** * struct irq_chip - 中断控制器芯片抽象 * 这个结构体定义了中断控制器的所有硬件操作函数 */ static struct irq_chip xxx_irq_chip { /* 芯片名称,会显示在 /proc/interrupts 中 */ .name 芯片名称, /* 应答中断 - 清除pending标志,告诉硬件已收到中断 * 调用时机: 边沿触发中断发生时立即调用 * 典型操作: 写寄存器清除IRQ_PENDING位 */ .irq_ack xxx_irq_ack, /* 屏蔽中断 - 禁止硬件产生此中断信号 * 调用时机: * 1. 电平触发中断处理前(避免重入) * 2. 驱动调用 disable_irq() * 典型操作: 设置IRQ_MASK寄存器对应位为1 */ .irq_mask xxx_irq_mask, /* 使能中断 - 允许硬件产生此中断信号 * 调用时机: * 1. 中断处理完成后 * 2. 驱动调用 enable_irq() * 典型操作: 清除IRQ_MASK寄存器对应位 */ .irq_unmask xxx_irq_unmask, /* 中断结束标记 - 通知硬件可以接收下一次中断 * 调用时机: 中断处理流程的最后一步 * 典型操作: 写IRQ_EOI寄存器 */ .irq_eoi xxx_irq_eoi, }; /* * 第二部分链式中断处理函数 (核心) * 作用: 当父中断触发时,识别并分发到具体子中断 * */ /** * xxx_chained_handler - 链式中断处理函数 * desc: 父中断的描述符(irq_desc) * * 执行上下文: 硬中断上下文,必须快速执行 * * 执行流程: * GIC检测到parent_irq → CPU跳转到这里 → 读硬件寄存器识别hwirq * → 调用对应子中断的handler → 返回 */ static void xxx_chained_handler(struct irq_desc *desc) { /* 获取父中断的chip操作函数集(通常是GIC的chip) */ struct irq_chip *chip irq_desc_get_chip(desc); int hwirq; /* 步骤1: 进入链式处理上下文 */ /** * chained_irq_enter() 执行两个关键操作: * 1. 调用父chip的 irq_mask_ack() - 应答并屏蔽父中断 * 目的: 防止在处理过程中父中断再次触发(避免重入) * 2. 标记当前正在处理此中断 */ chained_irq_enter(chip, desc); /* 步骤2: 读取硬件寄存器,识别具体的hwirq */ /** * 读取中断状态寄存器(IRQ_STATUS/PENDING寄存器) * * 寄存器格式示例(假设32位寄存器,每位对应一个子中断): * Bit31 ... Bit3 Bit2 Bit1 Bit0 * [---] ... [IRQ3][IRQ2][IRQ1][IRQ0] * * 如果Bit11, 表示hwirq1的中断正在pending * 可能同时有多个bit为1,需要逐个处理 */ hwirq 读取中断状态寄存器(); // 例如: hwirq ffs(readl(base STATUS_REG)) - 1; /* 步骤3: hwirq转换为virq,并触发其处理流程 */ /** * irq_find_mapping() - 在IRQ Domain中查找hwirq对应的virq * * 映射关系示例: * hwirq0 → virq150 (Linux分配的虚拟中断号) * hwirq1 → virq151 * hwirq2 → virq152 * * generic_handle_irq(virq) 会执行: * 1. 找到 irq_desc[virq] * 2. 调用 irq_desc[virq].handle_irq (通常是handle_level_irq) * 3. handle_level_irq内部会: * a) 调用 chip-irq_mask() - 屏蔽此子中断 * b) 遍历action链表,执行驱动注册的handler * c) 调用 chip-irq_unmask() - 恢复此子中断 * d) 调用 chip-irq_eoi() - 标记处理完成 */ generic_handle_irq(irq_find_mapping(domain, hwirq)); /* 步骤4: 退出链式处理上下文 */ /** * chained_irq_exit() 执行: * 1. 调用父chip的 irq_unmask() - 恢复父中断使能 * 目的: 允许下一次父中断触发 * 2. 清除正在处理标记 */ chained_irq_exit(chip, desc); } /* * 第三部分IRQ Domain 映射操作 * 作用: 管理hwirq到virq的映射关系 * */ /** * xxx_irq_map - virq初始化回调函数 * d: IRQ Domain指针 * virq: Linux虚拟中断号 * hwirq: 硬件中断号 * * 调用时机: * - irq_domain_add_legacy() 创建domain时自动调用 * - irq_create_mapping() 动态创建映射时调用 * * 作用: 为每个virq初始化其irq_desc结构 */ static int xxx_irq_map(struct irq_domain *d, unsigned int virq, irq_hw_number_t hwirq) { /* 步骤1: 设置chip级别的私有数据 */ /** * irq_set_chip_data() - 为virq关联chip的私有数据 * * 参数1 (virq): 虚拟中断号 * 参数2 (h-host_data): IRQ Domain的私有数据指针 * - 通常在 irq_domain_add_xxx() 时传入 * - 包含硬件控制器的寄存器基地址、配置信息等 * * 作用: 将私有数据保存到 irq_desc[virq].irq_data.chip_data * * 为什么重要: * chip操作函数(irq_mask/unmask/ack等)执行时需要访问硬件寄存器, * 必须知道寄存器基地址。chip_data就是传递这些信息的桥梁。 */ irq_set_chip_data(virq, d-host_data); /* 步骤2: 设置chip和handler */ /** * irq_set_chip_and_handler() - 设置中断处理的两个核心要素 * * 参数1 (virq): 要设置的虚拟中断号 * * 参数2 (xxx_irq_chip): 指定硬件操作函数集 * - 后续中断触发时会调用此chip的 mask/unmask/ack/eoi 函数 * * 参数3 (handle_level_irq): 指定中断处理流程 * - handle_level_irq: 电平触发中断的标准流程 * 流程: mask中断 → 执行action链表 → unmask中断 → eoi * 适用: 低电平/高电平触发的中断 * * - handle_edge_irq: 边沿触发中断的标准流程 * 流程: ack中断 → 执行action链表 → eoi * 适用: 上升沿/下降沿触发的中断 * * 如何选择: * - 电平触发选 handle_level_irq (需要先mask防止重复触发) * - 边沿触发选 handle_edge_irq (ack后自动清除,无需mask) */ irq_set_chip_and_handler(virq, xxx_irq_chip, handle_level_irq); // 或 handle_edge_irq return 0; } /** * IRQ Domain操作函数集 */ static const struct irq_domain_ops xxx_domain_ops { /** * xlate - 设备树中断说明符解析函数 * * irq_domain_xlate_onetwocell: 支持单cell或双cell格式 * 设备树示例: interrupts 1 IRQ_TYPE_EDGE_RISING; * 解析结果: hwirq1, type边沿上升 * * irq_domain_xlate_twocell: 仅支持双cell * irq_domain_xlate_onecell: 仅支持单cell */ .xlate irq_domain_xlate_onetwocell, /** * map - virq映射初始化函数 * 在创建hwirq→virq映射时调用 */ .map xxx_irq_map, }; /* * 第四部分驱动初始化流程 * 作用: 注册链式中断控制器到系统 * */ static int xxx_probe(struct platform_device *pdev) { int parent_irq, irq_base; struct irq_domain *domain; /* 步骤1: 获取父中断号 */ /** * platform_get_irq() - 从设备树获取父中断号 * * 对应设备树配置: * xxx_intc: interrupt-controller1000 { * compatible xxx,intc; * interrupt-parent gic; // 父控制器是GIC * interrupts GIC_SPI 140 ...; // 连接到GIC的140号中断 * }; * * 返回值: Linux虚拟中断号(如300),对应GIC的140号硬件中断 */ parent_irq platform_get_irq(pdev, 0); /* 步骤2: 设置链式处理函数 */ /** * irq_set_chained_handler_and_data() - 核心配置函数 * * 参数1 (parent_irq): 父中断的virq * 参数2 (xxx_chained_handler): 链式处理函数指针 * 参数3 (私有数据): 传递给handler的上下文数据 * * 执行操作: * 1. 设置 irq_desc[parent_irq].handle_irq xxx_chained_handler * - 替换原有handler(通常是GIC的handler) * - 当parent_irq触发时,CPU会调用xxx_chained_handler * * 2. 设置 irq_desc[parent_irq].handler_data 私有数据 * - 保存自定义数据指针 * - 在handler中通过 irq_desc_get_handler_data() 获取 * * 重要限制: * - 不能对同一parent_irq同时使用此函数和 request_irq() * - 原因: request_irq会设置action链表,而链式handler不使用action */ irq_set_chained_handler_and_data(parent_irq, xxx_chained_handler, 私有数据); /* 步骤3: 分配中断描述符 */ /** * irq_alloc_descs() - 为子中断分配irq_desc数组 * * 参数1 (-1): 自动选择起始virq (推荐) * 也可指定具体值,如100,但容易冲突 * * 参数2 (0): 期望的virq搜索的起始位置 * 实际分配可能这个值 * * 参数3 (子中断数量): 需要连续分配多少个描述符 * 例如: 4表示分配4个连续的irq_desc * * 参数4 (numa_node_id()): NUMA节点ID * 单CPU系统可填-1 * * 返回值: 分配的起始virq (如150) * * 结果: * 创建了 irq_desc[150], irq_desc[151], irq_desc[152], irq_desc[153] * 这些描述符尚未初始化,需要后续map操作 */ irq_base irq_alloc_descs(-1, 0, 子中断数量, numa_node_id()); /* 步骤4: 创建IRQ Domain */ /** * 方式一不推荐 * irq_domain_add_legacy() - 创建legacy类型的IRQ Domain * * 参数1 (设备节点): 设备树node指针 * 参数2 (子中断数量): domain管理的中断数量 * 参数3 (irq_base): 起始virq (步骤3分配的) * 参数4 (0): 起始hwirq (通常从0开始) * 参数5 (xxx_domain_ops): domain操作函数集 * 参数6 (私有数据): 传递给map函数的数据 * * 执行操作: * 1. 创建IRQ Domain对象 * * 2. 建立映射表: * hwirq0 → virq150 * hwirq1 → virq151 * hwirq2 → virq152 * hwirq3 → virq153 * * 3. 对每个映射调用 xxx_domain_ops.map (xxx_irq_map) * - 初始化irq_desc[virq] * - 设置chip和handle_irq * * 其他domain类型: * - irq_domain_add_linear: 线性映射,更常用 * - irq_domain_add_tree: 树形映射,适合稀疏hwirq空间 * - legacy适合有固定irq_base的老代码迁移 */ domain irq_domain_add_legacy(设备节点, 子中断数量, irq_base, 0, xxx_domain_ops, 私有数据); /** * 方式二推荐方式不需要进行步骤三 * irq_domain_add_linear() - 创建线性映射类型的IRQ Domain * * 函数原型: * struct irq_domain *irq_domain_add_linear( * struct device_node *of_node, * unsigned int size, * const struct irq_domain_ops *ops, * void *host_data * ) * * 参数1 (of_node): 设备树节点指针 * - 来自 pdev-dev.of_node * - 用于设备树中的 interrupt-parent 引用 * - 可以为NULL (非设备树场景) * * 参数2 (size): hwirq的最大数量 * - 定义hwirq的有效范围: 0 ~ (size-1) * - 例如: size4 表示支持 hwirq 0, 1, 2, 3 * - 不是virq数量! * * 参数3 (ops): domain操作函数集指针 * - 必须实现 .map 回调函数 * - 可选实现 .xlate 回调 (默认使用 irq_domain_xlate_onecell) * - 例如: xxx_domain_ops * * 参数4 (host_data): 私有数据指针 * - 传递给 map() 函数使用 * - 通常包含硬件寄存器基地址等信息 * - 可通过 d-host_data 访问 * - 可以为NULL * * 返回值: * - 成功: 返回 struct irq_domain * 指针 * - 失败: 返回 NULL */ domain irq_domain_add_linear(设备节点, 子中断数量, xxx_domain_ops, 私有数据); return 0; }方式1: irq_domain_add_linear (动态映射)时间线: probe阶段: ├─ 创建domain ✓ └─ 映射表为空 (size0) 设备树解析阶段 (gpio_keys驱动probe时): └─ 遇到 interrupts 2 ... └─ 自动创建 hwirq2 → virq150 的映射 另一个设备使用hwirq0: └─ 再创建 hwirq0 → virq151 的映射 如果hwirq1, 3从未被使用: └─ 永远不会分配virq (节省资源)方式2: irq_domain_add_legacy (静态映射)时间线: probe阶段: ├─ irq_alloc_descs() 预分配virq 100-103 ✓ ├─ 创建domain ✓ └─ 立即建立完整映射表: hwirq0 → virq100 ✓ hwirq1 → virq101 ✓ hwirq2 → virq102 ✓ hwirq3 → virq103 ✓ 设备树解析阶段: └─ 遇到 interrupts 2 ... └─ 直接查表返回 virq102 (不再动态创建) 即使hwirq1, 3从未使用: └─ virq101, 103 仍然被占用 (浪费资源)c、中断处理流程[硬件产生中断] ↓ [GIC检测到parent_irq300] ↓ [CPU读取GIC寄存器,跳转到irq_desc[300].handle_irq] ↓ [执行 xxx_chained_handler] ← 步骤2设置的函数 │ ├─ chained_irq_enter(chip, desc) │ └─ 调用GIC的chip-irq_mask_ack() // 屏蔽父中断 │ ├─ hwirq 读硬件寄存器() // 例如读到hwirq1 │ ├─ virq irq_find_mapping(domain, 1) // 查表得virq151 │ ├─ generic_handle_irq(151) │ └─ 调用 irq_desc[151].handle_irq ← 步骤4.map设置的handle_level_irq │ │ │ ├─ chip-irq_mask() ← 调用xxx_irq_chip.irq_mask │ ├─ 遍历action链表,执行驱动的handler │ ├─ chip-irq_unmask() ← 调用xxx_irq_chip.irq_unmask │ └─ chip-irq_eoi() ← 调用xxx_irq_chip.irq_eoi │ └─ chained_irq_exit(chip, desc) └─ 调用GIC的chip-irq_unmask() // 恢复父中断 ↓ [返回用户空间]2、层级中断控制器a、设备树配置只有次级中断控制器配置不同/* 父中断控制器 (GIC) */ gic: interrupt-controller8000000 { compatible arm,gic-400; #interrupt-cells 3; interrupt-controller; reg 0x8000000 0x1000, 0x8010000 0x2000; }; /* 层级中断控制器 */ xxx_intc: interrupt-controller1000000 { compatible xxx,intc; reg 0x1000000 0x1000; /* 声明为中断控制器 */ interrupt-controller; #interrupt-cells 2; /* hwirq flags */ /* 连接到GIC的140号SPI中断 */ interrupt-parent gic; /* 关键: 定义hwirq偏移量 - 映射到GIC的起始hwirq */ upper_hwirq_base 100; }; /* 使用此层级中断控制器的设备 */ my_device: device2000000 { compatible xxx,device; reg 0x2000000 0x1000; /* 使用xxx_intc的1号子中断 */ interrupt-parent xxx_intc; interrupts 1 IRQ_TYPE_EDGE_RISING; };b、驱动框架/*━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ * 第一部分全局数据结构定义 *━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━*/ /* 层级中断控制器的IRQ Domain */ static struct irq_domain *xxx_domain; /* 父域hwirq起始偏移量 * 示例upper_hwirq_base 100 * hwirq0 → 父hwirq100 * hwirq2 → 父hwirq102 */ static u32 upper_hwirq_base; /*━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ * 第二部分IRQ Chip操作函数透传机制 *━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━*/ /** * 中断屏蔽函数 - 透传到父域 * d: 层级中断控制器的irq_data * * 执行流程 * 层级中断控制器irq_data → irq_chip_mask_parent() → 父域irq_data → GIC硬件操作 */ static void xxx_irq_mask(struct irq_data *d) { /* * 屏蔽层级中断控制器的中断硬件操作 */ printk(%s: Masking IRQ %lu (hwirq%lu)\n, __func__, d-irq, d-hwirq); /* 核心API透传到父域的mask函数 * 内部实现 * 1. 获取 parent_data d-parent_data * 2. 调用 parent_data-chip-irq_mask(parent_data) */ irq_chip_mask_parent(d); } /** * 中断使能函数 - 透传到父域 * d: 层级中断控制器的irq_data */ static void xxx_irq_unmask(struct irq_data *d) { /* * 使能层级中断控制器的中断硬件操作 */ printk(%s: Unmasking IRQ %lu (hwirq%lu)\n, __func__, d-irq, d-hwirq); /* 透传到父域的unmask函数 */ irq_chip_unmask_parent(d); } /** * IRQ Chip结构体 - 定义中断控制器操作接口 * * 注意 * - 只需实现基本的mask/unmask * - 其他操作如eoi、set_type可根据需要添加 * - 所有操作都应使用irq_chip_xxx_parent()透传 */ static struct irq_chip xxx_chip { .name xxx, .irq_mask xxx_irq_mask, .irq_unmask xxx_irq_unmask, /* 可选操作 * .irq_eoi irq_chip_eoi_parent, * .irq_set_type irq_chip_set_type_parent, * .irq_set_affinity irq_chip_set_affinity_parent, */ }; /*━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ * 第三部分IRQ Domain操作函数 *━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━*/ /** * 设备树中断说明符解析函数 * d: IRQ Domain指针 * fwspec: 设备树中断说明符来自interrupts属性 * hwirq: 输出参数解析得到的层级中断控制器hwirq * type: 输出参数解析得到的触发类型 * * 设备树示例 * device_node { * interrupt-parent xxx; * interrupts 2 IRQ_TYPE_LEVEL_HIGH; * }; * * 解析规则 * - #interrupt-cells 2 * - param[0] hwirq号 * - param[1] 触发类型 */ static int xxx_domain_translate(struct irq_domain *d, struct irq_fwspec *fwspec, unsigned long *hwirq, unsigned int *type) { /* 只支持设备树节点 */ if (is_of_node(fwspec-fwnode)) { /* 检查参数数量必须为2 */ if (fwspec-param_count ! 2) return -EINVAL; /* 提取层级中断控制器hwirq和触发类型 */ *hwirq fwspec-param[0]; *type fwspec-param[1]; return 0; } return -EINVAL; } /** * 中断分配函数 - 层级控制器的核心 * domain: 层级中断控制器的IRQ Domain * irq: 分配的Linux虚拟中断号 * nr_irqs: 需要分配的中断数量通常为1 * data: 设备树中断说明符irq_fwspec * * 功能 * 1. 配置层级中断控制器的irq_desc[irq] * 2. 映射到父域并配置父层的irq_desc[irq] * * 返回值0表示成功负值表示错误 */ static int xxx_domain_alloc(struct irq_domain *domain, unsigned int irq, unsigned int nr_irqs, void *data) { struct irq_fwspec *fwspec data; struct irq_fwspec parent_fwspec; irq_hw_number_t hwirq; int i; /*──────────────────────────────────────────────────────────── * 步骤1设置层级中断控制器的irq_desc[irq]配置irq_data *────────────────────────────────────────────────────────────*/ hwirq fwspec-param[0]; /* 获取hwirq */ for (i 0; i nr_irqs; i) { /* 设置irq_desc[irq].irq_data * - hwirq字段 获得的hwirq * - chip字段 xxx_chip * - domain字段 本domain */ irq_domain_set_hwirq_and_chip(domain, irq i, hwirq i, xxx_chip, NULL); /* host_data为NULL */ } /*──────────────────────────────────────────────────────────── * 步骤2构造父域中断说明符 *────────────────────────────────────────────────────────────*/ /* 初始化父域fwspec */ parent_fwspec.fwnode domain-parent-fwnode; parent_fwspec.param_count 3; /* GIC需要3个参数 */ /* GIC中断说明符格式#interrupt-cells 3 * param[0]: 中断类型0SPI, 1PPI * param[1]: hwirq号相对于中断类型 * param[2]: 触发类型和标志 */ parent_fwspec.param[0] GIC_SPI; /* 0 共享外设中断 */ /* 关键层级中断控制器hwirq到父hwirq的映射 * 公式父hwirq 层级中断控制器hwirq upper_hwirq_base * * 示例upper_hwirq_base100 * hwirq0 → GIC hwirq100 * hwirq2 → GIC hwirq102 */ parent_fwspec.param[1] fwspec-param[0] upper_hwirq_base; /* 透传触发类型 */ parent_fwspec.param[2] fwspec-param[1]; /*──────────────────────────────────────────────────────────── * 步骤3配置父域建立层级关系 *────────────────────────────────────────────────────────────*/ /* 核心APIirq_domain_alloc_irqs_parent() * * 执行操作 * 1. 调用父domain-ops-alloc()即GIC的alloc函数 * 2. 设置irq_desc[irq].irq_data.parent_data指向GIC层 * 3. 设置irq_desc[irq].handle_irq使用GIC的handle_fasteoi_irq * * 注意这是层级控制器与链式控制器的关键区别 * - 层级使用父域的handle_irq * - 链式需要注册chained_handler */ return irq_domain_alloc_irqs_parent(domain, irq, nr_irqs, parent_fwspec); } /** * IRQ Domain操作函数集 * * 必需操作 * - translate: 解析设备树中断说明符 * - alloc: 分配并配置中断 * * 可选操作 * - free: 释放中断资源通常不需要 * - activate: 激活中断通常不需要 * - deactivate: 去激活中断通常不需要 */ static const struct irq_domain_ops xxx_domain_ops { .translate xxx_domain_translate, .alloc xxx_domain_alloc, /* .free xxx_domain_free, */ }; /*━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ * 第四部分Platform驱动接口 *━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━*/ /** * 驱动探测函数 - 初始化层级中断控制器 * pdev: Platform设备指针 * * 执行流程 * 1. 读取设备树配置 * 2. 查找父IRQ Domain * 3. 创建层级IRQ Domain */ static int xxx_probe(struct platform_device *pdev) { struct irq_domain *parent_domain; struct device_node *parent; int err; /*──────────────────────────────────────────────────────────── * 步骤1读取设备树配置 *────────────────────────────────────────────────────────────*/ /* 读取upper_hwirq_base属性 * 设备树示例 * xxx { * compatible vendor,virtual-intc; * upper_hwirq_base 100; * }; */ err of_property_read_u32(pdev-dev.of_node, upper_hwirq_base, upper_hwirq_base); /*──────────────────────────────────────────────────────────── * 步骤2查找父IRQ Domain *────────────────────────────────────────────────────────────*/ /* 根据interrupt-parent属性查找父节点 * 设备树示例 * xxx { * interrupt-parent gic; * }; */ parent of_irq_find_parent(pdev-dev.of_node); /* 根据父节点查找对应的IRQ Domain */ parent_domain irq_find_host(parent); /*──────────────────────────────────────────────────────────── * 步骤3创建层级IRQ Domain核心 *────────────────────────────────────────────────────────────*/ /* 核心APIirq_domain_add_hierarchy() * * 与链式控制器的关键区别 * - 不使用 irq_domain_add_linear() * - 不调用 irq_set_chained_handler_and_data() * - 通过parent参数建立层级关系 * * 参数说明 * parent: 父域指针GIC的domain * flags: 标志位通常为0 * size: 支持的hwirq数量这里是4个0-3 * of_node: 设备树节点 * ops: domain操作函数集 * host_data: 私有数据可为NULL */ xxx_domain irq_domain_add_hierarchy( parent_domain, /* 父域 */ 0, /* 标志 */ 4, /* hwirq范围: 0-3 */ pdev-dev.of_node, /* 设备树节点 */ xxx_domain_ops, /* 操作函数集 */ NULL /* 私有数据 */ ); return 0; }
版权声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

电器网站模板网站色彩搭配技巧

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容: 为完全不懂编程的用户设计一个最简单的翻译插件模板,要求:1.界面只有翻译按钮和结果框 2.使用浏览器默认API翻译 3.提供完整的使用说明注释 4.代码不超过100…

张小明 2025/12/27 16:18:04 网站建设

广州市建设集团网站首页wordpress 文章最长

在AI论文工具的选择上,8款热门平台针对降重、降低AIGC检测率及论文写作等核心功能进行了实测对比,结合效率、准确性和用户体验的综合评估,以下排名基于客观数据与真实反馈得出,帮助用户快速匹配需求。排名工具名称关键优势1aibiye…

张小明 2025/12/27 16:18:02 网站建设

做网站渠道面试网站建设需要的简历

2025年12月9日,OpenAI发布了备受期待的ChatGPT-5.2版本。这一版本不仅在技术上进行了深度优化,更在多个方面实现了前所未有的突破,进一步改变了我们与AI互动的方式。在刚刚发布的这一版本中,ChatGPT-5.2的表现堪称一场AI领域的“革…

张小明 2025/12/27 16:18:00 网站建设

做网站的IDE广东建的电商网站叫啥

脚本开发与调试技巧全解析 1. 脚本开发任务 在脚本开发过程中,我们常常会遇到各种需求,以下是一些具体的开发任务及相关说明。 1.1 网络管道脚本 netpipe 编写一个名为 netpipe 的脚本,它的作用是充当网络管道。不同机器上的 shell 脚本可以调用 netpipe 进行通信,就像…

张小明 2025/12/27 16:17:56 网站建设

怎么自己弄网站免费国内自动化网站建设

C 最強武器:利用類型系統實現零成本抽象C 的真正威力不僅在於指針和低級控制,更在於其強大的類型系統。通過類型系統,我們可以寫出既快速又安全的代碼,實現所謂的「零成本抽象」。1. 類型系統的核心優勢1.1 編譯時檢查cpp// 傳統做…

张小明 2026/1/4 7:52:14 网站建设

郑州专门做喷绘安装的网站工程建设项目在哪个网站查询

Langchain-Chatchat部署所需硬件资源配置建议(含GPU型号推荐) 在企业智能问答系统逐步从“通用助手”向“私有知识中枢”演进的今天,如何在保障数据安全的前提下实现高效、精准的语义理解与响应,已成为技术选型的核心命题。开源项…

张小明 2025/12/27 18:28:42 网站建设