当前位置: 首页 > news >正文

Linux 中断控制器驱动程序浅析

前言

  1. 因为之前师傅让我编写了一个 I2C 扩展 GPIO 控制器,但是这个 GPIO 控制器并没有要求编写中断的内容,所以就一直搁浅了。后面突发奇想,感觉还是要了解一下中断控制器的驱动程序如何编写,因此就有了该篇博客。
  2. 在韦东山老师的驱动大全中,其实已经介绍了中断控制器的驱动程序如何编写。但是我后面简单了解了一下,发现韦东山老师的中断驱动都是参考的 IMX6ULL 的中断控制器驱动。而在一些特殊的项目中,可能会要求外扩中断控制器,例如 ste-nomadik-nhk15 开发板就采用了两个 I2C 外扩的中断控制器。
i2c0 {stmpe0: stmpe2401@43 {compatible = "st,stmpe2401";reg = <0x43>;// ...stmpe_gpio43: stmpe_gpio {compatible = "st,stmpe-gpio";gpio-controller;#gpio-cells = <2>;interrupt-controller;// ...};};stmpe1: stmpe2401@44 {compatible = "st,stmpe2401";reg = <0x44>;// ...stmpe_gpio44: stmpe_gpio {compatible = "st,stmpe-gpio";gpio-controller;#gpio-cells = <2>;interrupt-controller;//...};};// ...
};
  1. 在 ARM Cortex-A和Cortex-M中断处理浅析 一文中,我简单介绍了 Cortex-A 和Cortex-M 中断处理机制。我们知道了对于 Cortex-A 核的中断处理,需要软件实现。
  2. Linux 做了一套中断处理框架,需要驱动工程师按照这套框架进行适配。这里,我将会简单介绍一下,如果你希望向 Linux 中注册一个 中断控制器,应该如何做。
  3. 个人邮箱:zhangyixu02@gmail.com
  4. 微信公众号:风正豪

在这里插入图片描述

中断控制器驱动程序

重要结构体

  1. 所谓的 Linux 驱动程序,本质上就是按照 Linux 内核开发者规定的框架进行填鸭。这里我将介绍比较重要的结构体,作为驱动开发者,如果要编写一个中断控制器要填充的结构体成员有哪些。
  2. irq_chip 结构体重要成员:
  • irq_chip.irq_ack : 当中断产生时,将会调用该成员函数清除中断源信号,标记该中断被处理,为新的相同中断触发做准备。无特殊需求,可以直接调用 Linux 提供的 irq_gc_ack_set_bit 函数。
  • irq_chip.irq_mask : 中断产生,需要调用该成员函数屏蔽相同的中断源,防止同一个中断执行过程中再次被自己中断导致重入问题。无特殊需求,可以直接调用 Linux 提供的 irq_gc_mask_set_bit 函数。
  • irq_chip.irq_mask_ack : 是 irq_chip.irq_ackirq_chip.irq_mask 之和。如果定义了这个成员,那么 irq_chip.irq_ackirq_chip.irq_mask 就没有必要再进行定义。
  • irq_chip.irq_unmask : 当中断执行完成之后,我们需要调用该函数解除对该中断的中断源屏蔽解除。无特殊需求,可以直接调用 Linux 提供的 irq_gc_mask_clr_bit 函数。
  • irq_chip.irq_set_type : 当注册中断时,设置中断的触发类型时将会调用该函数。这个必须提供,并且必须由芯片原厂提供,因为不同的中断控制器其中断触发类型不同。
  1. 需要支持 irq_gc_mask_clr_bitirq_gc_mask_set_bitirq_gc_ack_set_bit 函数就需要填充 irq_chip_type.regs 中的 ackmask 成员。
/*** struct irq_chip_regs - 中断控制器寄存器偏移量结构体* @enable:        使能寄存器相对于 reg_base 的偏移量* @disable:        禁用寄存器相对于 reg_base 的偏移量* @mask:        屏蔽寄存器相对于 reg_base 的偏移量* @ack:        确认寄存器相对于 reg_base 的偏移量* @eoi:        结束中断寄存器相对于 reg_base 的偏移量* @type:        类型配置寄存器相对于 reg_base 的偏移量* @polarity:        极性配置寄存器相对于 reg_base 的偏移量*/
struct irq_chip_regs {unsigned long                enable;    // 使能寄存器的偏移量unsigned long                disable;   // 禁用寄存器的偏移量unsigned long                mask;      // 屏蔽寄存器的偏移量unsigned long                ack;       // 确认寄存器的偏移量unsigned long                eoi;       // 结束中断寄存器的偏移量unsigned long                type;      // 类型配置寄存器的偏移量unsigned long                polarity;  // 极性配置寄存器的偏移量
};

成员变量被调用时机

  1. 设备驱动调用 request_irq 将会导致 irq_chip.irq_unmask 被调用用以使能中断。
  2. 如果是电平触发的函数,在函数执行之前,会调用 irq_chip.irq_maskirq_chip.irq_ack 来屏蔽中断,执行完成后调用 irq_chip.irq_unmask 重新使能中断。
if (chip->irq_mask_ack) {chip->irq_mask_ack(&desc->irq_data);
} else {chip->irq_mask(&desc->irq_data);if (chip->irq_ack)chip->irq_ack(&desc->irq_data);
}
  1. 屏蔽中断可以发送在外设、中断控制器、CPU 三个位置。
  • 设备驱动层调用 local_irq_disablelocal_irq_enable 针对的不是外部的中断控制器,而是直接让 CPU 自身不响应中断请求。
  • 设备驱动层调用 disable_irqenable_irq 针对的是中断控制器。当调用 disable_irq ,那么将会暂时屏蔽中断,在内核中的实现层面是做了延后屏蔽,直到 enable_irq 再执行 ISR。
  • 外设端的中断信号产生高度依赖外设本身,因此 Linux 并不会提供标准的 API,而是由外设驱动自行设计。

GIC 中断控制器

  1. GIC 是中断管理控制器,是直接与 CPU 核连接的。该控制器驱动程序是由 ARM 官方提供,如果是作为芯片原厂的工程师,是无需关心的。对于绝大多数人也接触不到,因此我不做介绍。(笑,水字,狗头)
  2. 如果你还是希望学习阅读 GIC 控制器如何编写,请自行阅读 Linux-4.9.88\drivers\irqchip\irq-gic.c

GPIO 级联中断控制器

  1. 对于 GPIO 级联中断控制器就是挂载在 GPIO 控制器下的中断控制器,例如前言中所说的 I2C GPIO 外扩中断控制器就属于级联在 GPIO 控制器下的中断控制器。
  2. 这类中断控制器可以由芯片原厂工程师编写,也可以由方案商的驱动工程师根据项目需求进行编写。对于想要学习这类中断控制器驱动编写的,可以参考 Linux-4.9.88\drivers\gpio\gpio-stmpe.cLinux-4.9.88\drivers\gpio\gpio-pca953x.c

设备树

i2c0 {stmpe0: stmpe2401@43 {compatible = "st,stmpe2401";reg = <0x43>;// ...stmpe_gpio43: stmpe_gpio {compatible = "st,stmpe-gpio";gpio-controller;#gpio-cells = <2>;interrupt-controller;// ...};};stmpe1: stmpe2401@44 {compatible = "st,stmpe2401";reg = <0x44>;// ...stmpe_gpio44: stmpe_gpio {compatible = "st,stmpe-gpio";gpio-controller;#gpio-cells = <2>;interrupt-controller;//...};};// ...
};

驱动程序

  1. 如下为一个简单的 GPIO 级联中断控制器的框架,请自行参考上述两个文件对照这个框架学习。
// 如下内容可以在设备树总设置,为了方便理解,这里用宏定义代替
#define chip0               0
#define chip1               1
#define extend_irq_chip_id  chip0// 定义一个结构体 extend_irq_chip,表示片外中断控制器的中断芯片信息
static struct irq_chip extend_irq_chip = {.name            = "extend_irq_chip",    // 中断芯片的名称.irq_mask        = extend_irq_mask,      // 中断屏蔽函数,为了防止中断函数真正执行,此时相同中断触发导致出现问题.irq_unmask      = extend_irq_unmask,    // 中断解除屏蔽函数,函数执行完成,解除屏蔽.irq_bus_lock    = extend_irq_bus_lock,  // 中断总线锁定函数,用于在访问中断总线时加锁,因为是片外控制器,需要防止多中断重入问题.irq_bus_sync_unlock = extend_irq_bus_sync_unlock, // 中断总线同步解锁函数,用于在访问完中断总线后解锁.irq_set_type    = extend_irq_set_type,  // 设置中断类型的函数,例如边缘触发或电平触发.irq_shutdown    = extend_irq_shutdown,  // 中断关闭函数,用于在设备关闭时处理相关中断
};// 中断处理函数
static irqreturn_t extend_irq_handler(int irq, void *devid)
{if (devid == chip0) {// 执行 chip0 相关操作} else if (devid == chip1) {// 执行 chip1 相关操作} else {return IRQ_NONE;}return IRQ_HANDLED;
}static int extend_irq_probe(struct i2c_client *client,const struct i2c_device_id *i2c_id)
{struct gpio_chip *gc;/* 1. 对相关参数进行初始化* 因为需要级联到 GPIO 控制器中,所以这里需要通过设备树设置当前芯片的父级芯片 */gc->parent = &client->dev;/* 2. 注册中断处理函数。* 片外中断控制器肯定是挂载在一个 GPIO 引脚上。因此我们需要注册这个引脚的中断* CPU 与片外中断控制器通讯属于慢速通讯,所以中断顶半部就不注册函数,* 所有函数处理推移至中断线程化的下半部。* 因为该驱动程序可能支持多个芯片,所以需要传入 extend_irq_chip_id 用于标识不同的芯片*/devm_request_threaded_irq(&client->dev,client->irq,NULL,extend_irq_handler,IRQF_TRIGGER_LOW | IRQF_ONESHOT |IRQF_SHARED,dev_name(&client->dev), extend_irq_chip_id);/* 3. 我们需要将片外的中断控制器级联到 GPIO 控制器,因此设置它的 IRQ 域在 GPIO 控制器之下* 该函数最终执行效果本质上就是给片外中断控制器设置好 irq_domain 域,这个域在 GPIO 控制器下* acpi_gpio_deferred_req_irqs_list 是一个全局可见的链表,也就是挂载在 GPIO 控制器下的 irq_domain 域*//*static LIST_HEAD(acpi_gpio_deferred_req_irqs_list);gpiochip_irqchip_add_nested-> gpiochip_irqchip_add_key-> acpi_gpiochip_request_interrupts-> list_add(&acpi_gpio->deferred_req_irqs_list_entry,&acpi_gpio_deferred_req_irqs_list);*/gpiochip_irqchip_add_nested(gc,&extend_irq_chip,0,handle_simple_irq,IRQ_TYPE_NONE);/* 4. 设置级联中断处理函数 *//*gpiochip_set_nested_irqchip-> gpiochip_set_cascaded_irqchip-> irq_set_chained_handler_and_data*/gpiochip_set_nested_irqchip(gc,&extend_irq_chip,client->irq);
}

链式中断控制器

  1. 这个名词是韦东山老师发明的,所以我这里也直接用他所创作的这个名词了。
  2. 下图为链式后中断控制器和层级中断控制器的定义,对于链式中断就是说,中断控制器只会给 GIC 控制器发送一个中断信号,但是链式中断控制器里面还有很多细分的中断信号需要判断。上面的 GPIO 级联中断控制器也就是链式中断控制器的一种,只不过这里的链式中断控制器是直接连接的 GIC,而 GPIO 级联中断控制器是连接的 GPIO 中断控制器。
  3. 层级式中断控制器就比较方便理解,级联中断控制器会像 GIC 中断控制器发送多个中断信号,每个中断信号对应一个中断事件,就不再需要进行细分。

在这里插入图片描述

设备树

/{virtual_intc: virtual_intc_100ask {compatible = "100ask,virtual_intc";reg = <0x0209c000 0x4000>;interrupt-controller;#interrupt-cells = <2>;interrupt-parent = <&intc>;interrupts = <GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>;};gpio_keys_100ask {compatible = "100ask,gpio_key";interrupt-parent = <&virtual_intc>;interrupts = <0 IRQ_TYPE_LEVEL_HIGH>,<1 IRQ_TYPE_LEVEL_HIGH>,<2 IRQ_TYPE_LEVEL_HIGH>,<3 IRQ_TYPE_LEVEL_HIGH>;};};

驱动程序

  1. 实话实说,感觉韦东山老师的链式中断控制器的示例代码有一点点小小的问题。也可能是我理解上存在偏差,这里是我参考 imx6ull 和 rk3568 的 GPIO 中断控制器写的一个简单的示例代码。各位可参考 Linux-4.9.88\drivers\gpio\gpio-mxc.c 配合如下示例代码学习。
static struct irq_domain *gpio_irq_domain;struct private_data {// ...
};/* 中断函数 */
static void gpio_irq_handler(struct irq_desc *desc)
{u32 irq_stat;// 因为驱动会需要匹配多设备,通过私有数据进行适配struct private_data *port = irq_desc_get_handler_data(desc);struct irq_chip *chip = irq_desc_get_chip(desc);// 该函数会调用 irq_desc[].irq_data.chip.irq_mask 或 irq_mask_ack 来屏蔽中断chained_irq_enter(chip, desc);// 读取中断控制器的寄存器,知道具体的发生了哪一个中断gpio_hwirq = get_gpio_hwirq();// 调用 irq_desc[].handle_irq。使用根据硬件中断号和中断域找到具体的中断函数generic_handle_irq(irq_find_mapping(port->domain, gpio_hwirq));// 该函数会调用 irq_desc[].irq_data.chip.irq_unmask 来重新使能中断chained_irq_exit(chip, desc);
}static int mxc_gpio_probe(struct platform_device *pdev)
{struct device_node *np = pdev->dev.of_node;struct resource *iores;      // 寄存器物理地址信息void __iomem *virbase;       // 中断寄存器物理地址映射后的虚拟地址int gic_hwirq,irq_base;      // GIC 中断号;中断描述符struct irq_chip_generic *gc; // 芯片的通用信息描述结构体/* 1. 从设备树中获取相关资源 *//* 1.1 从设备树 interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>; 获取 GIC 中断号* 指示当前中断控制器共享外设中断,要用 55 号中断*/gic_hwirq = platform_get_irq(pdev, 0);/* 1.2 从设备树 reg = <0x0209c000 0x4000>; 获取寄存器物理地址信息,并将其进行映射 */iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);virbase = devm_ioremap_resource(&pdev->dev, iores);/* 2. 根据获取到的 GIC 中断号,设置它的 irq_desc[].handle_irq* 当该中断控制器产生中断信号,CPU 会根据 GIC 中断号跳转到注册的 gpio_irq_handler 函数中*/irq_set_chained_handler_and_data(gic_hwirq,gpio_irq_handler,NULL);/* 3. 分配注册一个 irq_domain ,有如下三种方法* irq_domain_add_legacy* irq_domain_add_linear* irq_domain_add_hierarchy     *//* 3.1 方法1:动态分配中断描述符* &pdev->dev :指向设备结构体的指针,表示请求中断的设备* -1 : 不指定起始中断号,内核将自动选择可用的中断号* 0 : 分配的中断号的起始偏移量* 4 :分配 4 个连续的中断描述符,该中断控制器支持 4 个中断* numa_node_id() : 内存和处理器的访问速度可能会有所不同,因此在分配中断时指定 NUMA 节点可以帮助优化性能*/irq_base = devm_irq_alloc_descs(&pdev->dev, -1, 0, 4, numa_node_id());/* 3.1 注册一个 irq_domain(IRQ 域) */gpio_irq_domain = irq_domain_add_legacy(np, 4, irq_base, 0,&irq_domain_simple_ops, NULL);/* 3.2 方法2:注册一个 irq_domain(IRQ 域),这个是只有使用的时候才会分配,更灵活 */gpio_irq_domain = irq_domain_add_linear(np, 4,&irq_generic_chip_ops, NULL);/* 4. 为中断域分配芯片信息资源,并进行初始化* rk : irq_alloc_domain_generic_chips + irq_get_domain_generic_chip* mxc : devm_irq_alloc_generic_chip* 这里传入 handle_level_irq 表示中断是电平触发,所以会调用 irq_mask 用于屏蔽中断* 如果传入的是 handle_edge_irq ,那么中断是边沿触发,那么 irq_mask 将不会被调用 */gc = devm_irq_alloc_generic_chip(&pdev->dev, "gpio-test", 1, irq_base,virbase, handle_level_irq);gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;gc->chip_types[0].chip.irq_set_type = ...; // 芯片原厂实现,当设备驱动层调用注册中断类型时的处理方法gc->chip_types[0].regs.ack = ...;  // 确认寄存器相对于 reg_base 的偏移量gc->chip_types[0].regs.mask = ...; // 屏蔽寄存器相对于 reg_base 的偏移量/* 5. 注册中断,本质上调用的都是 __irq_do_set_handler 函数* rk : irq_set_chained_handler_and_data* mxc : devm_irq_setup_generic_chip*//*
irq_set_chained_handler_and_data-> irq_get_desc_buslock-> __irq_do_set_handler*//*
devm_irq_setup_generic_chip-> irq_setup_generic_chip-> irq_set_chip_and_handler-> irq_set_chip_and_handler_name-> irq_set_chip-> __irq_set_handler-> __irq_do_set_handler*/devm_irq_setup_generic_chip(&pdev->dev, gc, IRQ_MSK(4),IRQ_GC_INIT_NESTED_LOCK,IRQ_NOREQUEST, 0);
}

层级中断控制器

设备树


#define m 123/{virtual_intc: virtual_intc_100ask {compatible = "100ask,virtual_intc";interrupt-controller;#interrupt-cells = <2>;interrupt-parent = <&intc>;// GIC 中断号起始位置upper_hwirq_base = <122>;  // imx6ull//upper_hwirq_base = <210>;  // stm32mp157};gpio_keys_100ask {compatible = "100ask,gpio_key";interrupt-parent = <&virtual_intc>;interrupts = <0 IRQ_TYPE_LEVEL_HIGH>,<1 IRQ_TYPE_LEVEL_HIGH>,<2 IRQ_TYPE_LEVEL_HIGH>,<3 IRQ_TYPE_LEVEL_HIGH>;};};

驱动程序

  1. 层级式中断控制器就很简单了,因为他们不涉及到函数分发的问题。这里直接给韦东山老师的示例代码了。
  2. 各位可以配合 Linux-4.9.88\arch\arm\mach-imx\gpc.c 学习。
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/random.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/gpio/driver.h>
/* FIXME: for gpio_get_value() replace this with direct register read */
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_device.h>
#include <linux/bug.h>static struct irq_domain *virtual_intc_domain;
static u32 upper_hwirq_base;static int virtual_intc_domain_translate(struct irq_domain *d,struct irq_fwspec *fwspec,unsigned long *hwirq,unsigned int *type)
{// 检查 fwspec 是否是设备树节点if (is_of_node(fwspec->fwnode)) {/* 如果参数数量不是 2,则返回错误,interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;* 对于 imx6ull 的 gpc 节点来说,#interrupt-cells = <3>;* 因此改在在 gpc 节点下的中断需要有 3 个参数,interrupts = <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>;*/if (fwspec->param_count != 2)return -EINVAL;// 提取硬件中断号 (hwirq) 和中断类型 (type)*hwirq = fwspec->param[0];*type = fwspec->param[1];return 0;}return -EINVAL;
}static void virtual_intc_irq_unmask(struct irq_data *d)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);irq_chip_unmask_parent(d);
}static void virtual_intc_irq_mask(struct irq_data *d)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);irq_chip_mask_parent(d);
}static void virtual_intc_irq_eoi(struct irq_data *d)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);irq_chip_eoi_parent(d);
}static struct irq_chip virtual_intc_chip = {.name			= "virtual_intc",.irq_mask		= virtual_intc_irq_mask,.irq_unmask 	= virtual_intc_irq_unmask,.irq_eoi        = virtual_intc_irq_eoi,
};static int virtual_intc_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;/* 设置irq_desc[irq] *//* 1. 设置irq_desc[irq].irq_data, 里面含有virtual_intc irq_chip */hwirq = fwspec->param[0];for (i = 0; i < nr_irqs; i++)irq_domain_set_hwirq_and_chip(domain, irq + i, hwirq + i,&virtual_intc_chip, NULL);/* 2. 设置irq_desc[irq].handle_irq,  来自GIC */parent_fwspec.fwnode = domain->parent->fwnode;/* 因为该中断控制器是挂载在 GIC 下,而 GIC 节点 #interrupt-cells = <3>;* 但是韦东山老师编写的虚拟中断控制器设备树示例里面没有 interrupts 属性,因此这里需要手动指定相关值* 如果指定了 interrupts 属性,例如 imx6ul.dtsi 中的 gpc 节点。那么驱动程序就可以写成 parent_fwspec = *fwspec;*/parent_fwspec.param_count = 3; // 3个参数parent_fwspec.param[0]    = 0; // GIC_SPI,共享中断parent_fwspec.param[1]    = fwspec->param[0] + upper_hwirq_base; // 加上基地址获取到实际的硬件中断号parent_fwspec.param[2]    = fwspec->param[1]; // 中断类型return irq_domain_alloc_irqs_parent(domain, irq, nr_irqs,&parent_fwspec);
}static const struct irq_domain_ops virtual_intc_domain_ops = {.translate	= virtual_intc_domain_translate, // 解析设备树中的 interrupts = <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>;.alloc		= virtual_intc_domain_alloc,
};static int virtual_intc_probe(struct platform_device *pdev)
{	struct irq_domain *parent_domain;struct device_node *parent;int err;printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);// 获取层级中断的硬件中断号基地址// upper_hwirq_base = <122>;err = of_property_read_u32(pdev->dev.of_node, "upper_hwirq_base", &upper_hwirq_base);parent = of_irq_find_parent(pdev->dev.of_node);parent_domain = irq_find_host(parent);/* 分配/设置/注册irq_domain */virtual_intc_domain = irq_domain_add_hierarchy(parent_domain, 0, 4,pdev->dev.of_node, &virtual_intc_domain_ops,NULL);return 0;
}
static int virtual_intc_remove(struct platform_device *pdev)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}static const struct of_device_id virtual_intc_of_match[] = {{ .compatible = "100ask,virtual_intc", },{ },
};static struct platform_driver virtual_intc_driver = {.probe		= virtual_intc_probe,.remove		= virtual_intc_remove,.driver		= {.name	= "100ask_virtual_intc",.of_match_table = of_match_ptr(virtual_intc_of_match),}
};/* 1. 入口函数 */
static int __init virtual_intc_init(void)
{	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);/* 1.1 注册一个platform_driver */return platform_driver_register(&virtual_intc_driver);
}/* 2. 出口函数 */
static void __exit virtual_intc_exit(void)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);/* 2.1 反注册platform_driver */platform_driver_unregister(&virtual_intc_driver);
}module_init(virtual_intc_init);
module_exit(virtual_intc_exit);MODULE_LICENSE("GPL");

调试

  1. 我们可以执行如下命令查看中断控制器注册情况。
$ cd /sys/kernel/irq/[虚拟中断号]
# 查看中断控制器的名称
$ cat chip_name
# 查看硬件中断号
$ cat hwirq
  1. 编写一个设备驱动程序,查看是否能够成功注册中断。
cat /proc/interrupts
  1. 使用 devmem 工具修改 GIC 寄存器的值,手动产生中断信号。

参考

  1. 韦东山驱动大全
http://www.xdnf.cn/news/122797.html

相关文章:

  • 解决ROS2安装过程中无法连接raw.githubusercontent.com的问题
  • 黑马 redis面试篇笔记
  • [web]攻防世界 easyphp
  • 第1讲:Transformers 的崛起:从RNN到Self-Attention
  • AlphaGo 究竟是如何通过深度学习和强化学习自主学习棋局策略的?
  • Vue 3 的核心组合式 API 函数及其完整示例、使用场景和总结表格
  • 《从混乱到有序:ArkUI项目文件结构改造指南》
  • YOLO训练时到底需不需要使用权重
  • Ubuntu / WSL 安装pipx
  • Kingbase性能优化浅谈
  • 书籍推荐:《价值心法》一姜胡说
  • Selenium 怎么加入代理IP,以及怎么检测爬虫运行的时候,是否用了代理IP?
  • ospf综合作业
  • kubernetes》》k8s》》Dashboard
  • rocky9.4部署k8s群集v1.28.2版本(containerd)(纯命令)
  • 运维打铁:Centos 7使用yum安装 Redis 5
  • 模糊控制Fuzzy control
  • 《深入理解计算机系统》阅读笔记之第一章 计算机系统漫游
  • 面试之消息队列
  • OpenAI为何觊觎Chrome?AI时代浏览器争夺战背后的深层逻辑
  • Phthon
  • 智能吸顶灯/摄影补光灯专用!FP7195双通道LED驱动,高效节能省空间 !
  • 学员答题pk知识竞赛小程序怎么做
  • 视频汇聚平台EasyCVR赋能高清网络摄像机:打造高性价比视频监控系统
  • C++如何理解和避免ABA问题?在无锁编程中如何解决
  • 对话模型和补全模型区别
  • 聊聊Spring AI Alibaba的OneNoteDocumentReader
  • 【C/C++干货】VS Code 快捷键大全
  • 线上助农产品商城小程序源码介绍
  • 测试模版x