// 8259A 中的寄存器: // ICW: Initialization Command Word,初始化命令寄存器 // OCW: Operation Command Word,操作命令字,用于控制 8259A // IRR: Interrupt Request Register,中断请求寄存器,共 8bit,对应 IR0~IR7 八个中断管脚。当某个管脚的中断请求到来后, // 若该管脚没有被屏蔽,IRR 中对应的 bit 被置1。表示 PIC 已经收到设备的中断请求,但还未提交给 CPU。 // ISR: In Service Register,服务中寄存器,共 8bit,每 bit 意义同上。当 IRR 中的某个中断请求被发送给 CPU 后,ISR中对应的 bit 被置1, // 表示中断已发送给 CPU,但 CPU 还未处理完。 // IMR: Interrupt Mask Register,中断屏蔽寄存器,共 8bit,每 bit 意义同上。用于屏蔽中断。当某 bit 置1时,对应的中断管脚被屏蔽。 // 注:8259a通过ICW1的bit3位设置触发方式,0为边沿触发,1为电平触发 // 中断描述符 1.1 typedef struct irq_desc { hw_irq_controller *handler; //中断控制器 void *handler_data; struct irqaction *action; //共享同一irq的处理例程 unsigned int status; //当前中断状态 unsigned int depth; //中断关闭的嵌套深度 unsigned int irq_count; unsigned int irqs_unhandled; spinlock_t lock; //保护锁 } ____cacheline_aligned irq_desc_t; // 中断控制器 1.2 struct hw_interrupt_type { //中断控制器名字 const char * typename; //开启irq unsigned int (*startup)(unsigned int irq); //关闭irq void (*shutdown)(unsigned int irq); //激活一个irq,执行irq由禁用状态到启用状态的转换 void (*enable)(unsigned int irq); //禁用irq void (*disable)(unsigned int irq); //ack与中断控制器的硬件密切相关,在某些模型中,irq请求到达时必须显示确认, //后续的请求才能进行处理,如果芯片组没有这样的要求,设置该函数为null void (*ack)(unsigned int irq); //调用end标记中断处理的结束,如果一个中断在中断处理期间被禁用,那么该函数负责启用此类中断 void (*end)(unsigned int irq); //在多处理器上,可使用set_affinity指定CPU来处理特定的IRQ, //使得可以将IRQ分配给某些CPU。在单处理器上,此函数为null。 void (*set_affinity)(unsigned int irq, cpumask_t dest); }; // i8259a中断控制器 2.1 static struct hw_interrupt_type i8259A_irq_type = { "XT-PIC", //名字为XT-PIC startup_8259A_irq, shutdown_8259A_irq, enable_8259A_irq, disable_8259A_irq, mask_and_ack_8259A, end_8259A_irq, NULL }; // 开启irq // 通过enable开启irq 2.2 unsigned int startup_8259A_irq(unsigned int irq) { enable_8259A_irq(irq); return 0; } // 激活irq // 函数主要任务: // 1.计算对应irq bit的mask // 2.设置imr寄存器 // 2.1 如果irq号 > 7,设置从片的imr // 2.2 设置主片的imr // i8259a通过两个byte存放irq的mask // 1.irq号 > 7,取第二个byte,即cached_slave_mask // 2.irq号 < 7, 取第一个byte // // 注:imr中对应位置为0,中断开启;为1,中断屏蔽 2.3 void enable_8259A_irq(unsigned int irq) { //对应irq的bit设置为0 unsigned int mask = ~(1 << irq); unsigned long flags; //互斥使用中断控制器 spin_lock_irqsave(&i8259A_lock, flags); //记录所有被屏蔽的irq位置 cached_irq_mask &= mask; //irq号>7,从片的中断 if (irq & 8) { //从片的IMR outb(cached_slave_mask, PIC_SLAVE_IMR); } else { //主片的IMR outb(cached_master_mask, PIC_MASTER_IMR); } spin_unlock_irqrestore(&i8259A_lock, flags); } // 关闭irq // 1.计算对应irq bit的mask // 2.设置主或从片的imr 2.4 void disable_8259A_irq(unsigned int irq) { unsigned int mask = 1 << irq; unsigned long flags; spin_lock_irqsave(&i8259A_lock, flags); cached_irq_mask |= mask; if (irq & 8) outb(cached_slave_mask, PIC_SLAVE_IMR); else outb(cached_master_mask, PIC_MASTER_IMR); spin_unlock_irqrestore(&i8259A_lock, flags); } // 屏蔽并确认中断 // 函数主要任务: // 1.检查是否为虚假中断 // 1.1 被屏蔽的中断发出中断请求,并且当前没有待处理的中断,说明为虚假中断 // 1.2 处理虚假中断 // 1.2.1 输出错误信息 // 1.2.2 统计虚假中断次数 // 2.如果为从片发生的中断 // 2.1 向从片的imr写入屏蔽的irq掩码 // 2.2 向从片的cmd应答对应的irq号 // 2.3 向主片的cmd应答irq2 // 3.如果为主片发生的中断 // 3.1 向主片的imr写入屏蔽的irq掩码 // 3.2 向主片的cmd应答对应的irq号 2.5 void mask_and_ack_8259A(unsigned int irq) { unsigned int irqmask = 1 << irq; unsigned long flags; spin_lock_irqsave(&i8259A_lock, flags); //irq已经被关闭,处理错误 if (cached_irq_mask & irqmask) goto spurious_8259A_irq; //屏蔽对应的irq cached_irq_mask |= irqmask; handle_real_irq: //从片irq if (irq & 8) { //读入旧值 inb(PIC_SLAVE_IMR); //imr写入irq屏蔽mask outb(cached_slave_mask, PIC_SLAVE_IMR); //从片cmd寄存器,对应IRQ写入EOI(end of interrupt) outb(0x60+(irq&7),PIC_SLAVE_CMD); //主片cmd寄存器,IRQ2写入EOI outb(0x60+PIC_CASCADE_IR,PIC_MASTER_CMD); } else { //读入旧值 inb(PIC_MASTER_IMR); //imr写入irq屏蔽mask outb(cached_master_mask, PIC_MASTER_IMR); //主片cmd寄存器,对应IRQ写入EOI outb(0x60+irq,PIC_MASTER_CMD); } spin_unlock_irqrestore(&i8259A_lock, flags); return; spurious_8259A_irq: //获取isr寄存器,判断是否是真实的中断 if (i8259A_irq_real(irq)) goto handle_real_irq; { //处理虚假的请求信号: static int spurious_irq_mask; //此irq第一次虚假中断请求 //打印出错信息 if (!(spurious_irq_mask & irqmask)) { printk(KERN_DEBUG "spurious 8259A interrupt: IRQ%d.\n", irq); spurious_irq_mask |= irqmask; } //增加出错计数 atomic_inc(&irq_err_count); //返回 goto handle_real_irq; } } // 中断处理结束 2.6 static void end_8259A_irq (unsigned int irq) { //中断没有被禁止,并且中断处理程序没有在运行,同时有可用的中断处理程序 if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)) && irq_desc[irq].action) { //激活irq对应的中断 enable_8259A_irq(irq); } } // i8259a级联图: