系统设计者可以自由选择中断结构,一般的IBM PC兼容将使用Intel 82C59A-2 CMOS可编程中断控制器或其派生者。这种控制器在PC诞生之前便已经产生,它的可编程性体现在那些位于众所周知ISA内存位置中的寄存器上。非Intel系统如基于Alpha AXP的PC不受这些体系结构限制,它们经常使用各种不同的中断控制器。
图7.1给出了两个级连的8位控制器,每个控制器都有一个屏蔽与中断状态寄存器:PIC1和PIC2。这两个屏蔽寄存器分别位于ISA I/O空间0x21和0xA1处,状态寄存器则位于0x20和0xA0。对此屏蔽寄存器某个特定位置位将使能某一中断,写入0则屏蔽它。但是不幸的是中断屏蔽寄存器是只写的,所以你无法读取你写入的值。这也意味着Linux必须保存一份对屏蔽寄存器写入值的局部拷贝。一般在中断使能和屏蔽例程中修改这些保存值,同时每次将这些全屏蔽码写入寄存器。
当有中断产生时,中断处理代码将读取这两个中断状态寄存器(ISR)。它将0x20中的ISR看成一个16位中断寄存器的低8位而将0xA0中的ISR看成其高8位。这样0xA0中ISR第1位上的中断将被视作系统中断9。PIC1 上的第二位由于被用来级连PIC2所以不能作其它用处,PIC2上的任何中断将导致PIC1的第二位被置位。
7.2 初始化中断处理数据结构核心的中断处理数据结构在设备驱动请求系统中断控制时建立。为完成此项工作,设备驱动使用一组Linux核心函数来请求中断,使能中断和屏蔽中断。
每个设备驱动将调用这些过程来注册其中断处理例程地址。
有些中断由于传统的PC体系结构被固定下来,所以驱动仅需要在其初始化时请求它的中断。软盘设备驱动正是使用的这种方式;它的中断号总为6。有时设备驱动也可能不知道设备使用的中断号。对PCI设备驱动来说这不是什么大问题,它们总是可以知道其中断号。但对于ISA设备驱动则没有取得中断号的方便方式。Linux通过让设备驱动检测它们的中断号来解决这个问题。
设备驱动首先迫使设备引起一个中断。系统中所有未被分配的中断都被使能。此时设备引发的中断可以通过可编程中断控制器来发送出去。Linux再读取中断状态寄存器并将其内容返回给设备驱动。非0结果则表示在此次检测中有一个或多个中断发生。设备驱动然后将关闭检测并将所有未分配中断屏蔽掉。
如果ISA设备驱动成功找到了设备的IRQ号,就可以象平常一样请求对设备的控制。
基于PCI系统比基于ISA系统有更多的动态性。ISA设备使用的中断引脚通常是通过硬件设备上的跳线来设置并固定在设备驱动中。PCI设备在系统启动与初始化PCI时由PCI BIOS或PCI子系统来分配中断。每个PCI设备可以使用A,B,C或D之中的任意中断。这个中断在设备建立时确定且通常多数设备的缺省中断为 A。PCI槽中的PCI中断连线A,B,C和D被正确路由到中断控制器中。所以PCI槽4上的引脚A可能被路由到中断控制器上的引脚6,PCI槽7上的引脚B被路由到中断控制器上的引脚7等等。
如何路由PCI中断完全取决于特定的系统,一般设置代码能理解PCI中断路由拓扑。在基于Intel的PC上由系统BIOS代码在启动时作这些设置而在不带BIOS(如Alpha AXP)系统中由Linux核心来完成这个任务。
PCI设置代码将每个设备对应的中断控制器的引脚号写入PCI配置头中。通过得到PCI中断路由拓扑及设备的PCI槽号和PCI中断引脚设置代码可以确定其对应的中断引脚(或IRQ)号。设备使用的中断引脚被保存在此设备的PCI配置头中为此目的保留的中断连线域中。当运行设备驱动时这些信息被读出并用来控制来自Linux核心的中断请求。
系统中可能存在许多PCI中断源,比如在使用PCI-PCI桥接器时。这些中断源的个数可能将超出系统可编程中断控制器的引脚数。此时PCI设备必须共享中断号-中断控制器上的一个引脚可能被多个PCI设备同时使用。Linux让中断的第一个请求者申明此中断是否可以共享。中断的共享将导致irq_action数组中的一个入口同时指向几个irqaction数据结构。当共享中断发生时Linux将调用对应此中断源的所有中断处理过程。没有中断需要服务时,任何共享此中断(所有的PCI设备驱动)的设备驱动都要准备好其中断处理过程的调用。
7.3 中断处理 图7.2 Linux中断处理数据结构