由上图可以看到.所有中线引脚都是连在中断请求路径互连器(下面简称为PIR)上的,然后再由它控制到8259A的连接.有一点要特别注意.在APIC中是没有PIR的,因为APIC本身就是一个可编程控制器.在本节的讨论中,我们只讨论8259A的情况.
至于中断控制器的输入引脚是和哪一个8259A的IRQ线相联.这是由PIR芯片所控制的.
闲言少述,我们从代码中看一下是怎样处理这个IRQ号的.相应的处理是在pcibios_irq_init()中完成的.先看下它的调用方式:
subsys_initcall(pcibios_irq_init);
pcibios_irq_init()是在subsys_initcall宏被调用的.在编译的时候被放至了init区域,kernel启动的时候会调用此函数.
在分析函数之前,我们先要有以下几点基本的概念:
1: PIR其实也是一个PCI设备.所以它也有对应的总线号,设备号等.也可以通过PCI寻址方式对它进行配置
2: 在确定PCI设备的中断号之前,我们需要知道.PCI设备是连在PIR的哪一个输入线上,这也是一个复杂的过程,幸好bios为我们提供了这样的功能.
好了,转入具体的代码:
static int __init pcibios_irq_init(void)
{
DBG(KERN_DEBUG "PCI: IRQ init\n");
if (pcibios_enable_irq || raw_pci_ops == NULL)
return 0;
dmi_check_system(pciirq_dmi_table);
pirq_table = pirq_find_routing_table();
#ifdef CONFIG_PCI_BIOS
if (!pirq_table && (pci_probe & PCI_BIOS_IRQ_SCAN))
pirq_table = pcibios_get_irq_routing_table();
#endif
if (pirq_table) {
pirq_peer_trick();
pirq_find_router(&pirq_router);
if (pirq_table->exclusive_irqs) {
int i;
for (i=0; i<16; i++)
if (!(pirq_table->exclusive_irqs & (1 << i)))
pirq_penalty[i] += 100;
}
/* If we're using the I/O APIC, avoid using the PCI IRQ routing table */
if (io_apic_assign_pci_irqs)
pirq_table = NULL;
}
pcibios_enable_irq = pirq_enable_irq;
pcibios_fixup_irqs();
return 0;
}
函数先调用pirq_find_routing_table()来寻找bios提供的中断路径表,代码如下 :
static struct irq_routing_table * __init pirq_find_routing_table(void)
{
u8 *addr;
struct irq_routing_table *rt;
if (pirq_table_addr) {
rt = pirq_check_routing_table((u8 *) __va(pirq_table_addr));
if (rt)
return rt;
printk(KERN_WARNING "PCI: PIRQ table NOT found at pirqaddr\n");
}
for(addr = (u8 *) __va(0xf0000); addr < (u8 *) __va(0x100000); addr += 16) {
rt = pirq_check_routing_table(addr);
if (rt)
return rt;
}
return NULL;
}
如果指定了中断路径表的地址.那直接判断它是否合法就可以了.如果没有指定,那就需要搜索bios的映射区了.
转入pirq_check_routing_table().代码如下:
static inline struct irq_routing_table * pirq_check_routing_table(u8 *addr)
{
struct irq_routing_table *rt;
int i;
u8 sum;
rt = (struct irq_routing_table *) addr;
if (rt->signature != PIRQ_SIGNATURE ||
rt->version != PIRQ_VERSION ||
//是否低四位对齐
rt->size % 16 ||
rt->size < sizeof(struct irq_routing_table))
return NULL;
sum = 0;
//所有的值加起来必须为零.因为它后面多了一个checksum
for (i=0; i < rt->size; i++)
sum += addr[i];
if (!sum) {
DBG(KERN_DEBUG "PCI: Interrupt Routing Table found at 0x%p\n", rt);
return rt;
}
return NULL;
}
Bios提供的中断路径表有自己的格式.对应格式的结构如下所示 :
struct irq_routing_table {
//恒定义为PIRQ_SIGNATURE
u32 signature; /* PIRQ_SIGNATURE should be here */
//恒为PIRQ_VERSION
u16 version; /* PIRQ_VERSION */
//表的大小
u16 size; /* Table size in bytes */
//PIR的总线号和设备功能号
u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */
//分配给PIR专用的IRQ
u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */
//PIR芯片的厂商和设备号
u16 rt