内核如何掌控中断号?

中断号由内核统一分配和管理,确保不同硬件设备的中断请求互不冲突,维护系统稳定运行。

在嵌入式 Linux 驱动开发中,确定设备使用的中断号(IRQ number)是让设备能够响应硬件事件(如数据到达、状态改变)的关键步骤,这个中断号并非一个固定不变的硬件物理编号,而是 Linux 内核在运行时动态分配或映射的一个虚拟中断号,以下是确定中断号的详细过程和主要方法:

硬件设备通常有一个物理中断线(或称为硬件中断号、中断引脚),在 Linux 内核中,这个物理中断线会被映射到一个内核管理的虚拟中断号(irq,驱动开发者主要关心和使用的是这个内核分配的 irq 值。

确定中断号的主要方法:

现代嵌入式 Linux 开发(尤其是使用设备树 Device Tree 的体系结构)中,设备树(Device Tree)最主要、最推荐的方式。

  1. 通过设备树(Device Tree – 首选方法)

    • 原理: 设备树(.dts / .dtsi 文件)是一个描述硬件平台结构和资源(包括中断)的数据结构,它独立于内核源码,在系统启动时由 Bootloader(如 U-Boot)传递给内核。
    • 过程:
      1. 设备节点定义中断属性: 在描述你的硬件设备的设备树节点中,需要包含一个或多个 interrupts 属性,这个属性指定了该设备连接到哪个中断控制器(interrupt-parent)以及在该中断控制器上的哪个(或哪些)物理中断线/信号。
      2. 中断控制器节点: 系统中会有一个或多个中断控制器节点(如 intc, gic 等),它们定义了 #interrupt-cells 属性,说明其 interrupts 属性需要多少个 u32 单元来描述一个中断源,常见的值有 1, 2, 3。
      3. 内核解析与映射: 内核在启动时解析设备树,当它遇到一个带有 interrupts 属性的设备节点时:
        • 根据 interrupt-parent(或继承父节点的)找到对应的中断控制器节点。
        • 根据该中断控制器的 #interrupt-cells 值,读取 interrupts 属性中相应数量的 u32 值。
        • 这些 u32 值通常包含:物理中断号(硬件中断线编号)、中断触发类型(如边沿触发、电平触发、高/低有效等)、有时还有中断优先级/类型(取决于具体中断控制器)。
        • 内核根据这些信息,在内部为该物理中断线分配一个唯一的虚拟中断号 (irq),并建立映射关系。
    • 驱动获取中断号:
      • 在驱动代码的 probe 函数(当设备与驱动匹配成功时调用)中,使用 platform_get_irq()platform_get_irq_byname() 函数。
      • *`platform_get_irq(struct platform_device pdev, unsigned int num)`**:
        • pdev: 指向与该设备关联的 platform_device 结构体的指针(通常由 probe 函数参数传入)。
        • num: 设备树中该设备 interrupts 属性里定义的第几个中断(索引,从 0 开始),如果设备只有一个中断源,通常为 0。
        • 返回值: 成功时返回内核分配的虚拟中断号 (irq > 0);失败时返回负的错误码(如 -ENXIO 表示中断未定义)。
      • platform_get_irq_byname(struct platform_device *pdev, const char *name):
        • 如果设备树节点中的中断使用了 interrupt-names 属性给中断命名("tx", "rx", "int0"),则可以使用此函数按名称获取对应的中断号。
        • name: 要获取的中断在 interrupt-names 中指定的名称。
        • 返回值: 同上。
    • 示例 (设备树片段):
      &my_device { // 假设这是一个挂载在总线上的设备节点
          compatible = "vendor,my-device";
          reg = <0x10000000 0x1000>; // 寄存器地址范围
          interrupt-parent = <&intc>; // 指向中断控制器节点
          interrupts = <0 42 IRQ_TYPE_LEVEL_HIGH>; // 物理中断号=42, 高电平触发
          // 如果支持多个中断或有名字:
          // interrupts = <0 42 IRQ_TYPE_EDGE_RISING>, <0 43 IRQ_TYPE_LEVEL_LOW>;
          // interrupt-names = "primary", "secondary";
      };
    • 示例 (驱动代码片段):
      static int my_driver_probe(struct platform_device *pdev)
      {
          int irq, ret;
          ...
          // 获取第一个(索引0)中断号
          irq = platform_get_irq(pdev, 0);
          if (irq < 0) {
              dev_err(&pdev->dev, "Failed to get IRQ: %d\n", irq);
              return irq; // 返回错误
          }
          // 或者按名称获取 (如果定义了 interrupt-names)
          // irq = platform_get_irq_byname(pdev, "primary");
          ...
          // 使用获取到的 irq 注册中断处理函数
          ret = devm_request_irq(&pdev->dev, irq, my_interrupt_handler,
                                IRQF_SHARED, dev_name(&pdev->dev), my_private_data);
          if (ret) {
              dev_err(&pdev->dev, "Failed to request IRQ %d: %d\n", irq, ret);
              return ret;
          }
          ...
          return 0;
      }
  2. 传统/旧方法(不推荐,仅用于遗留系统或无设备树)

    • 板级特定文件(Board File): 在内核源码的板级支持包(BSP)或特定机器的文件(如 arch/arm/mach-xxx/board-yyy.c)中,通常会硬编码地为设备定义资源,包括中断号,驱动通过 platform_device 结构体获取这些资源。
    • 驱动获取中断号:
      • probe 函数中,使用 platform_get_resource(pdev, IORESOURCE_IRQ, index) 获取 struct resource *
      • 然后从 res->start 中提取中断号 (irq = res->start)。
    • 缺点: 高度依赖特定内核版本和板级代码,可移植性差,难以维护,现代嵌入式开发强烈建议使用设备树替代。
  3. 直接硬件查询(极其罕见,不推荐)

    • 理论上,驱动可以读取设备的某个配置寄存器来获取它使用的中断线(物理号),但这需要:
      • 精确知道该寄存器位置和格式。
      • 知道如何将该物理中断号映射到内核虚拟中断号(这通常需要访问特定于平台/中断控制器的内部数据结构,非常复杂且易变)。
    • 强烈不推荐: 这种方法破坏了内核的中断抽象层,可移植性极差,极易出错,几乎只在非常早期的内核或特殊裸机场景中使用。在标准 Linux 驱动开发中应避免。

关键点总结:

  1. 内核分配: 驱动使用的 irq 是内核管理的虚拟中断号,不是硬件的物理中断线号。
  2. 设备树是标准: 对于现代嵌入式 Linux 开发,设备树 (Device Tree) 是定义和获取中断号的绝对首选和标准方法。 使用 platform_get_irq()platform_get_irq_byname() 在驱动的 probe 函数中安全获取。
  3. 避免硬编码: 绝不要在驱动代码中硬编码中断号 (irq),这会导致驱动无法在不同硬件或不同配置上运行。
  4. 理解设备树绑定: 正确编写设备树节点是基础,需要查阅:
    • 设备的数据手册,了解其物理中断输出。
    • 所用中断控制器(如 GIC, NVIC, 外部中断控制器芯片)的文档和 Linux 内核绑定文档 (Documentation/devicetree/bindings/interrupt-controller/),了解其 #interrupt-cells 含义和 interrupts 属性的编码方式。
    • 硬件平台(SoC/板级)的参考设备树 (arch/arm64/boot/dts/vendor/ 等)。
  5. 调试工具: 系统启动后,可以通过 /proc/interrupts 文件查看所有已注册中断的统计信息,包括中断号、所属设备、触发次数等,是验证中断是否成功注册和触发的有力工具。

在嵌入式 Linux 驱动开发中,确定中断号的正确且现代的方式是:在设备树 (.dts) 中为你的设备节点明确定义 interrupt-parentinterrupts 属性,然后在驱动的 probe 函数中使用 platform_get_irq()platform_get_irq_byname() 函数获取内核分配的实际虚拟中断号 (irq)。 摒弃硬编码和直接操作硬件寄存器的方法,确保驱动的可移植性和可维护性。

引用说明:

  • 主要基于 Linux 内核文档 (尤其是关于设备树、中断和平台设备的文档,可在 Documentation/ 目录下找到)。
  • platform_get_irq() 等函数原型和用法参考 Linux 内核源码 (include/linux/platform_device.h, drivers/base/platform.c)。
  • 设备树绑定规范参考内核源码 Documentation/devicetree/bindings/ 下的相关文件。
  • 经典参考书籍如《Linux Device Drivers, 3rd Edition》(LDD3) 和《Essential Linux Device Drivers》提供了中断处理的基础知识,但需注意其中关于中断号获取的部分可能偏向旧方法,现代实践以设备树为主。

原创文章,发布者:酷番叔,转转请注明出处:https://cloud.kd.cn/ask/6586.html

(0)
酷番叔酷番叔
上一篇 2025年7月7日 14:11
下一篇 2025年7月7日 14:37

相关推荐

  • 如何安全添加官方PPA源?

    在Linux系统中升级PHP版本是确保网站安全、提升性能及支持新特性的关键操作,以下为详细步骤,涵盖主流发行版(Ubuntu/Debian、CentOS/RHEL),严格遵循系统管理最佳实践:升级前的关键准备备份数据 # 备份当前PHP配置和网站数据sudo cp -r /etc/php /etc/php_ba……

    6天前
    600
  • 如何检查系统防火墙服务异常?

    根据防火墙工具选择查看方式Linux有多种防火墙管理工具,需先确认系统使用的工具:若显示 active (running) 表示对应工具已启用(如 firewalld、ufw),具体查看方法firewalld(CentOS/RHEL/Fedora)查看运行状态:sudo systemctl status fir……

    2025年7月7日
    800
  • Ubuntu 24.04升级后apt报错?

    Ubuntu和Debian是流行的Linux发行版,Debian以其稳定性、严格的自由软件理念和庞大的软件仓库著称,Ubuntu基于Debian,更注重用户友好性、定期发布和商业支持,是新手和桌面用户的理想选择,两者都使用APT包管理系统。

    2025年6月30日
    800
  • Linux如何挂载U盘

    检测U盘设备插入U盘,执行命令识别设备:sudo fdisk -l观察输出结果,通常U盘显示为 /dev/sdb 或 /dev/sdc(末尾的 sdb1、sdc1 表示分区),通过容量和分区类型(如 FAT32、NTFS)确认U盘标识,使用lsblk快速查看:lsblk输出示例: sdb 8:16 1 14.9……

    2025年7月9日
    900
  • Linux进入home目录有哪些高效方法?

    通过命令行进入(最常用)使用 cd 命令打开终端(快捷键 Ctrl+Alt+T),输入:cd ~或cd $HOME原理: 是用户主目录的简写符号,$HOME 是环境变量,两者均指向 /home/用户名,直接指定路径若知道用户名(如用户名为 alice):cd /home/alice注意:需替换 alice 为你……

    2025年6月27日
    1300

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们

400-880-8834

在线咨询: QQ交谈

邮件:HI@E.KD.CN

关注微信