request_irq 失败,因为没有 irq 描述符
request_irq fails because there is no irq descriptor
我有一个 Linux 系统,我正在连接到自定义硬件。 (具体来说,这是一个带有FPGA的SoC)。
我正在尝试编写一个内核模块来响应来自我的设备的中断。它包含对 request_irq
的调用,但这是通过对 uio_register_device
的调用完成的。当我 运行 insmod
时,它失败并显示 "Invalid parameters".
在研究代码一段时间后,我最终在我的模块初始化函数中添加了这些行:
struct irq_desc *p;
p = irq_to_desc(89);
if (!p) {
printk(KERN_ALERT "No descriptor allocated for IRQ number 89!!\n");
}
p = irq_to_desc(121);
if (!p) {
printk(KERN_ALERT "No descriptor allocated for IRQ number 121!!\n");
}
果然,我在 dmesg
中看到了这些消息。
遇到这种情况我该怎么办?
我设法弄明白了。为了使中断正常工作,您需要 map 一个虚拟 IRQ 号(即您将在 request_interrupt
中使用的编号)到硬件 IRQ 号。当然,硬件 IRQ 号不是唯一的;例如,两个中断控制器的硬件 IRQ 号可能为 0。所以我真正的意思是,您必须将虚拟 IRQ 号与 irq_domain
的 组合 相关联和一个硬件 IRQ 号。
除此之外还有一点:irq_domain
s 不与中断控制器一一对应。一个控制器可以有多个域。
因此,让我们准确定义 "mapping" IRQ 的含义:
- 每个虚拟 IRQ 都有一个
struct irq_desc
。该结构的某些字段被映射为指向中断控制器驱动程序实现的功能。 (我不确定 Linux 在你做任何映射之前接收到中断时会做什么)
- 每个中断控制器可以提供一定数量的
struct irq_domain
s。这些本质上编码了硬件 IRQ 编号到虚拟 IRQ 编号之间的映射,并且有一个 API 供驱动程序查找编号。
irq_domain
结构还包含指向由中断控制器驱动程序实现的回调的指针。创建新映射时会触发这些回调之一;我认为这就是硬件配置为生成特定虚拟 IRQ 的方式。
当您请求中断时,您的 ISR 将被添加到 irq_desc
中的 actions
列表中,前提是 irq_desc
已正确初始化。
无论如何,我找到的在给定硬件 IRQ 编号的情况下获取虚拟 IRQ 编号(并在必要时创建新映射)的方法如下:
#include <linux/irqdomain.h> //For irq_domain struct
#include <linux/of.h> //For device tree struct types
#include <linux/irq.h> //Needed by other interrupt-related code
struct device_node *dn;
struct irq_domain *dom;
struct irq_fwspec dummy_fwspec = {
.param_count = 3,
.param = {0, 89, 4}
};
int virq;
//Find the Linux irq number
dn = of_find_node_by_name(NULL, "interrupt-controller");
if (!dn) {
printk(KERN_ERR "Could not find device node for \"interrupt-controller\"\n");
goto /*error_handler*/;
}
dom = irq_find_host(dn);
if (!dom) {
printk(KERN_ERR "Could not find irq domain\n");
goto /*error_handler*/;
}
dummy_fwspec.fwnode = dom->fwnode;
virq = irq_create_fwspec_mapping(&dummy_fwspec);
irq_create_fwspec_mapping
将 return 一个现有的(映射的)虚拟 IRQ 号,或者映射一个新的。 struct irq_fwspec
包含执行映射所需的所有信息。 fwnode
字段必须指向您正在使用的中断控制器;在我的例子中,我使用设备树 API 找到了正确的值(可能有更好的方法,但我不知道它是什么!)
这个例子是专门针对MPSoC的,我碰巧知道它使用了this驱动程序(drivers/irqchip/irq-gic.c)。我查看了驱动程序的源代码以弄清楚它在 irq_fwspec
结构的其他部分中想要什么。具体来说,每个控制器驱动程序将填充一个 struct irq_domain_ops
,因此 CTRL-F 可以找到您需要满足的回调函数。
在我的具体示例中,我知道 gic_irq_domain_alloc
会被调用,而且它还会调用 gic_irq_domain_translate
。这告诉我第二个数字是硬件 IRQ 号,但出于某种原因,32 将被添加到它。因为我想要硬件 IRQ 号 121,所以我插入了 89 作为第二个号码。第一个和第三个数字是根据我看到其他人所做的猜测。
如果有人知道更简单的方法,请告诉我
我有一个 Linux 系统,我正在连接到自定义硬件。 (具体来说,这是一个带有FPGA的SoC)。
我正在尝试编写一个内核模块来响应来自我的设备的中断。它包含对 request_irq
的调用,但这是通过对 uio_register_device
的调用完成的。当我 运行 insmod
时,它失败并显示 "Invalid parameters".
在研究代码一段时间后,我最终在我的模块初始化函数中添加了这些行:
struct irq_desc *p;
p = irq_to_desc(89);
if (!p) {
printk(KERN_ALERT "No descriptor allocated for IRQ number 89!!\n");
}
p = irq_to_desc(121);
if (!p) {
printk(KERN_ALERT "No descriptor allocated for IRQ number 121!!\n");
}
果然,我在 dmesg
中看到了这些消息。
遇到这种情况我该怎么办?
我设法弄明白了。为了使中断正常工作,您需要 map 一个虚拟 IRQ 号(即您将在 request_interrupt
中使用的编号)到硬件 IRQ 号。当然,硬件 IRQ 号不是唯一的;例如,两个中断控制器的硬件 IRQ 号可能为 0。所以我真正的意思是,您必须将虚拟 IRQ 号与 irq_domain
的 组合 相关联和一个硬件 IRQ 号。
除此之外还有一点:irq_domain
s 不与中断控制器一一对应。一个控制器可以有多个域。
因此,让我们准确定义 "mapping" IRQ 的含义:
- 每个虚拟 IRQ 都有一个
struct irq_desc
。该结构的某些字段被映射为指向中断控制器驱动程序实现的功能。 (我不确定 Linux 在你做任何映射之前接收到中断时会做什么) - 每个中断控制器可以提供一定数量的
struct irq_domain
s。这些本质上编码了硬件 IRQ 编号到虚拟 IRQ 编号之间的映射,并且有一个 API 供驱动程序查找编号。 irq_domain
结构还包含指向由中断控制器驱动程序实现的回调的指针。创建新映射时会触发这些回调之一;我认为这就是硬件配置为生成特定虚拟 IRQ 的方式。
当您请求中断时,您的 ISR 将被添加到 irq_desc
中的 actions
列表中,前提是 irq_desc
已正确初始化。
无论如何,我找到的在给定硬件 IRQ 编号的情况下获取虚拟 IRQ 编号(并在必要时创建新映射)的方法如下:
#include <linux/irqdomain.h> //For irq_domain struct
#include <linux/of.h> //For device tree struct types
#include <linux/irq.h> //Needed by other interrupt-related code
struct device_node *dn;
struct irq_domain *dom;
struct irq_fwspec dummy_fwspec = {
.param_count = 3,
.param = {0, 89, 4}
};
int virq;
//Find the Linux irq number
dn = of_find_node_by_name(NULL, "interrupt-controller");
if (!dn) {
printk(KERN_ERR "Could not find device node for \"interrupt-controller\"\n");
goto /*error_handler*/;
}
dom = irq_find_host(dn);
if (!dom) {
printk(KERN_ERR "Could not find irq domain\n");
goto /*error_handler*/;
}
dummy_fwspec.fwnode = dom->fwnode;
virq = irq_create_fwspec_mapping(&dummy_fwspec);
irq_create_fwspec_mapping
将 return 一个现有的(映射的)虚拟 IRQ 号,或者映射一个新的。 struct irq_fwspec
包含执行映射所需的所有信息。 fwnode
字段必须指向您正在使用的中断控制器;在我的例子中,我使用设备树 API 找到了正确的值(可能有更好的方法,但我不知道它是什么!)
这个例子是专门针对MPSoC的,我碰巧知道它使用了this驱动程序(drivers/irqchip/irq-gic.c)。我查看了驱动程序的源代码以弄清楚它在 irq_fwspec
结构的其他部分中想要什么。具体来说,每个控制器驱动程序将填充一个 struct irq_domain_ops
,因此 CTRL-F 可以找到您需要满足的回调函数。
在我的具体示例中,我知道 gic_irq_domain_alloc
会被调用,而且它还会调用 gic_irq_domain_translate
。这告诉我第二个数字是硬件 IRQ 号,但出于某种原因,32 将被添加到它。因为我想要硬件 IRQ 号 121,所以我插入了 89 作为第二个号码。第一个和第三个数字是根据我看到其他人所做的猜测。
如果有人知道更简单的方法,请告诉我