如何从 linux 内核驱动模块中的 hwirq 号找到我的 irq 号? (找到匹配 irq_desc)
How to find my irq number from hwirq number in linux kernel driver module? (finding matching irq_desc)
我正在 qemu arm64 虚拟机上进行 linux 驱动程序和应用程序测试。
我的虚拟机是 arm 'virt' 机器的略微修改版本,它包含我们的设备模型。
当我在我的 arm64 虚拟机上 linux 中为 INTID 208 (SPI 176) 执行 request_irq 时,它 returns -EINVAL。
我深入研究了那个函数,发现 desc 在函数 request_threaded_irq.
中是 returning NULL
int request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn, unsigned long irqflags,
const char *devname, void *dev_id)
{
struct irqaction *action;
struct irq_desc *desc;
int retval;
...
desc = irq_to_desc(irq);
if (!desc)
return -EINVAL;
...
}
和 irq_to_desc 应该 return 来自 irq_radix_tree 的 irq_desc,如下所示。
struct irq_desc *irq_to_desc(unsigned int irq)
{
return radix_tree_lookup(&irq_desc_tree, irq);
}
void *radix_tree_lookup(const struct radix_tree_root *root, unsigned long index)
{
return __radix_tree_lookup(root, index, NULL, NULL);
}
void *__radix_tree_lookup(const struct radix_tree_root *root,
unsigned long index, struct radix_tree_node **nodep,
void __rcu ***slotp)
{
struct radix_tree_node *node, *parent;
unsigned long maxindex;
void __rcu **slot;
restart:
parent = NULL;
slot = (void __rcu **)&root->xa_head;
radix_tree_load_root(root, &node, &maxindex);
if (index > maxindex)
return NULL;
...
}
发现我的request index是208,但是radix tree的maxindex是63。
所以看起来 irq_desc 的基数树已被设置为最大 64 irq_descs。
我在哪里以及如何设置这个基数树的大小?
我查看了 qemu 的 hw/arm/ab21q-build-acpi.c 的 MADT 或 DSDT table 部分,但找不到要添加的内容。
我在 DSDT 和 MADT 中添加了我的设备地址范围和 irq。
static void
build_dsdt(GArray *table_data, BIOSLinker *linker, Ab21qMachineState *vms)
{
...
acpi_dsdt_add_axpu(scope, &memmap[AB21Q_AXPU],
(irqmap[AB21Q_AXPU] + ARM_SPI_BASE));
...
}
static void acpi_dsdt_add_axpu(Aml *scope, const MemMapEntry *axpu_memmap,
uint32_t irq)
{
Aml *dev = aml_device("AXPU");
aml_append(dev, aml_name_decl("_HID", aml_string("AXPU0011")));
aml_append(dev, aml_name_decl("_UID", aml_int(0)));
Aml *crs = aml_resource_template();
aml_append(crs, aml_memory32_fixed(axpu_memmap->base,
axpu_memmap->size, AML_READ_WRITE));
aml_append(crs,
aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
AML_EXCLUSIVE, &irq, 1));
aml_append(dev, aml_name_decl("_CRS", crs));
aml_append(scope, dev);
}
我不确定是否可以将 _HID 值用作“AXPU0011”。
如果你看到什么不对的地方,有什么建议,请告诉我。
添加
这是与IRQ相关的虚拟机启动信息。
[ 0.000000] NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0
[ 0.000000] GICv3: 224 SPIs implemented
[ 0.000000] GICv3: 0 Extended SPIs implemented
[ 0.000000] GICv3: Distributor has no Range Selector support
[ 0.000000] GICv3: 16 PPIs implemented
[ 0.000000] GICv3: no VLPI support, no direct LPI support
[ 0.000000] GICv3: CPU0: found redistributor 0 region 0:0x00000000080a0000
不知何故 NR_IRQS 是 64。
所以我尝试使用未分配的 SPI 15。 (32 个内部 IRQ + 15 < 64)现在 request_irq returns 0.
所以这个 irq_desc 问题暂时解决了(?)。 (不知道为什么CONFIG_SPARSE_IRQS=y的时候这个这么小)
即使我设置了 qemu_set_irq.
,处理程序仍然没有被调用
添加
这是设备的 fdt 生成部分。
static void create_ab21q_axpu_device(const Ab21qMachineState *vms)
{
char *nodename;
printf("create_ab21q_axpu_device called!\n");
hwaddr base = vms->memmap[AB21Q_AXPU].base;
hwaddr size = vms->memmap[AB21Q_AXPU].size;
int irq = vms->irqmap[AB21Q_AXPU]; // irq = 15 (meaning SPI 15)
const char compat[] = "ab21q-axpu";
sysbus_create_simple("ab21q-axpu", base, qdev_get_gpio_in(vms->gic, irq));
nodename = g_strdup_printf("/ab21q_axpu@%" PRIx64, base);
qemu_fdt_add_subnode(vms->fdt, nodename);
qemu_fdt_setprop(vms->fdt, nodename, "compatible", compat, sizeof(compat));
qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", 2, base, 2, size);
qemu_fdt_setprop_cells(vms->fdt, nodename, "interrupts",
GIC_FDT_IRQ_TYPE_SPI, irq,
GIC_FDT_IRQ_FLAGS_LEVEL_HI);
qemu_fdt_setprop_cell(vms->fdt, nodename, "interrupt-parent", vms->gic_phandle);
g_free(nodename);
}
我找到了如何在我的案例中找到 irq 号! (char驱动内核模块)分享给大家参考
为此添加这两个头文件。
#include <linux/irq.h>
#include <linux/irqdesc.h>
extern struct irq_desc *irq_to_desc(unsigned int irq);
struct irq_desc *desc;
unsigned int virq;
并且在模块_init函数中:
// find my irq number
for(i=0;i<NR_IRQS;i++){
desc = irq_to_desc(i);
if (desc) {
//printk("irq_desc(%d)->irq_data.hwirq = %ld\n", i,
desc->irq_data.hwirq);
if (desc->irq_data.hwirq == 47) break; // 47 is the hwirq number, (SPI 15)
}
}
if (i == NR_IRQS) {
printk("couldn't find irq number..\n");
goto r_device; // destroy device class and chrdev region..
}
virq = i;
ret = request_irq(virq, axpu_irq_handler, IRQF_SHARED, "axpu_irq", &axpu_cdev);
并且在模块 _exit 函数中:
free_irq(virq, &axpu_cdev);
这样我就可以在 ubuntu 和 vanilla linux 上找到正确的 irq 号。
希望这对某人有帮助。 (反正你知道硬件连接就可以用了)
我正在 qemu arm64 虚拟机上进行 linux 驱动程序和应用程序测试。 我的虚拟机是 arm 'virt' 机器的略微修改版本,它包含我们的设备模型。 当我在我的 arm64 虚拟机上 linux 中为 INTID 208 (SPI 176) 执行 request_irq 时,它 returns -EINVAL。 我深入研究了那个函数,发现 desc 在函数 request_threaded_irq.
中是 returning NULLint request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn, unsigned long irqflags,
const char *devname, void *dev_id)
{
struct irqaction *action;
struct irq_desc *desc;
int retval;
...
desc = irq_to_desc(irq);
if (!desc)
return -EINVAL;
...
}
和 irq_to_desc 应该 return 来自 irq_radix_tree 的 irq_desc,如下所示。
struct irq_desc *irq_to_desc(unsigned int irq)
{
return radix_tree_lookup(&irq_desc_tree, irq);
}
void *radix_tree_lookup(const struct radix_tree_root *root, unsigned long index)
{
return __radix_tree_lookup(root, index, NULL, NULL);
}
void *__radix_tree_lookup(const struct radix_tree_root *root,
unsigned long index, struct radix_tree_node **nodep,
void __rcu ***slotp)
{
struct radix_tree_node *node, *parent;
unsigned long maxindex;
void __rcu **slot;
restart:
parent = NULL;
slot = (void __rcu **)&root->xa_head;
radix_tree_load_root(root, &node, &maxindex);
if (index > maxindex)
return NULL;
...
}
发现我的request index是208,但是radix tree的maxindex是63。 所以看起来 irq_desc 的基数树已被设置为最大 64 irq_descs。 我在哪里以及如何设置这个基数树的大小? 我查看了 qemu 的 hw/arm/ab21q-build-acpi.c 的 MADT 或 DSDT table 部分,但找不到要添加的内容。 我在 DSDT 和 MADT 中添加了我的设备地址范围和 irq。
static void
build_dsdt(GArray *table_data, BIOSLinker *linker, Ab21qMachineState *vms)
{
...
acpi_dsdt_add_axpu(scope, &memmap[AB21Q_AXPU],
(irqmap[AB21Q_AXPU] + ARM_SPI_BASE));
...
}
static void acpi_dsdt_add_axpu(Aml *scope, const MemMapEntry *axpu_memmap,
uint32_t irq)
{
Aml *dev = aml_device("AXPU");
aml_append(dev, aml_name_decl("_HID", aml_string("AXPU0011")));
aml_append(dev, aml_name_decl("_UID", aml_int(0)));
Aml *crs = aml_resource_template();
aml_append(crs, aml_memory32_fixed(axpu_memmap->base,
axpu_memmap->size, AML_READ_WRITE));
aml_append(crs,
aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
AML_EXCLUSIVE, &irq, 1));
aml_append(dev, aml_name_decl("_CRS", crs));
aml_append(scope, dev);
}
我不确定是否可以将 _HID 值用作“AXPU0011”。 如果你看到什么不对的地方,有什么建议,请告诉我。
添加
这是与IRQ相关的虚拟机启动信息。
[ 0.000000] NR_IRQS: 64, nr_irqs: 64, preallocated irqs: 0
[ 0.000000] GICv3: 224 SPIs implemented
[ 0.000000] GICv3: 0 Extended SPIs implemented
[ 0.000000] GICv3: Distributor has no Range Selector support
[ 0.000000] GICv3: 16 PPIs implemented
[ 0.000000] GICv3: no VLPI support, no direct LPI support
[ 0.000000] GICv3: CPU0: found redistributor 0 region 0:0x00000000080a0000
不知何故 NR_IRQS 是 64。
所以我尝试使用未分配的 SPI 15。 (32 个内部 IRQ + 15 < 64)现在 request_irq returns 0.
所以这个 irq_desc 问题暂时解决了(?)。 (不知道为什么CONFIG_SPARSE_IRQS=y的时候这个这么小)
即使我设置了 qemu_set_irq.
添加
这是设备的 fdt 生成部分。
static void create_ab21q_axpu_device(const Ab21qMachineState *vms)
{
char *nodename;
printf("create_ab21q_axpu_device called!\n");
hwaddr base = vms->memmap[AB21Q_AXPU].base;
hwaddr size = vms->memmap[AB21Q_AXPU].size;
int irq = vms->irqmap[AB21Q_AXPU]; // irq = 15 (meaning SPI 15)
const char compat[] = "ab21q-axpu";
sysbus_create_simple("ab21q-axpu", base, qdev_get_gpio_in(vms->gic, irq));
nodename = g_strdup_printf("/ab21q_axpu@%" PRIx64, base);
qemu_fdt_add_subnode(vms->fdt, nodename);
qemu_fdt_setprop(vms->fdt, nodename, "compatible", compat, sizeof(compat));
qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", 2, base, 2, size);
qemu_fdt_setprop_cells(vms->fdt, nodename, "interrupts",
GIC_FDT_IRQ_TYPE_SPI, irq,
GIC_FDT_IRQ_FLAGS_LEVEL_HI);
qemu_fdt_setprop_cell(vms->fdt, nodename, "interrupt-parent", vms->gic_phandle);
g_free(nodename);
}
我找到了如何在我的案例中找到 irq 号! (char驱动内核模块)分享给大家参考
为此添加这两个头文件。
#include <linux/irq.h>
#include <linux/irqdesc.h>
extern struct irq_desc *irq_to_desc(unsigned int irq);
struct irq_desc *desc;
unsigned int virq;
并且在模块_init函数中:
// find my irq number
for(i=0;i<NR_IRQS;i++){
desc = irq_to_desc(i);
if (desc) {
//printk("irq_desc(%d)->irq_data.hwirq = %ld\n", i,
desc->irq_data.hwirq);
if (desc->irq_data.hwirq == 47) break; // 47 is the hwirq number, (SPI 15)
}
}
if (i == NR_IRQS) {
printk("couldn't find irq number..\n");
goto r_device; // destroy device class and chrdev region..
}
virq = i;
ret = request_irq(virq, axpu_irq_handler, IRQF_SHARED, "axpu_irq", &axpu_cdev);
并且在模块 _exit 函数中:
free_irq(virq, &axpu_cdev);
这样我就可以在 ubuntu 和 vanilla linux 上找到正确的 irq 号。
希望这对某人有帮助。 (反正你知道硬件连接就可以用了)