如何从linux内核驱动模块中的hwirq号码中找到我的irq号码?(查找匹配的irq_desc)



我正在qemu arm64虚拟机上进行linux驱动程序和应用程序测试。我的虚拟机是arm"virt"机器的略微修改版本,它包含了我们的设备型号。当我在arm64虚拟机上的linux中为INTID 208(SPI 176(请求_irq时,它会返回-EINVAL。我深入研究了那个函数,发现desc在函数request_thread_idirq中返回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从irq_ radix_树返回irq_。

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;
...
}

我发现我的请求索引是208,但基数树的最大索引是63。因此,看起来irq_desc的基数树已设置为最大64个irq_descs。在哪里以及如何设置此基数树大小?我查看了qemu的hw/arm/ab21q构建的用于MADT或DSDT表部件的acpi.c,但找不到要添加的内容。我在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返回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 = %ldn", 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编号
希望这对某人有所帮助。(无论如何,你知道硬件连接,所以你可以使用它(

最新更新