字符设备驱动笔记——中断方式按键驱动之linux中断处理结构(五)
一、单片机下的中断处理 1)分辨是哪一个中断 2)调用处理函数 3)清中断 二、linux下的中断处理 1)/arch/arm/kernel/irq.c asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs) { struct pt_regs *old_regs = set_irq_regs(regs); struct irq_desc *desc = irq_desc + irq; /* * Some hardware gives randomly wrong interrupts. Rather * than crashing, do something sensible. */ if (irq >= NR_IRQS) desc = &bad_irq_desc; irq_enter(); desc_handle_irq(irq, desc); /* AT91 specific workaround */ irq_finish(irq); irq_exit(); set_irq_regs(old_regs); } 2)/kernel/irq/handle.c static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc) { desc->handle_irq(irq, desc); } 3)/kernel/irq/chip.c __set_irq_handler{ ... desc->handle_irq = handle; ... } 4)/include/linux/irq.h static inline void set_irq_handler(unsigned int irq, irq_flow_handler_t handle) { __set_irq_handler(irq, handle, 0, NULL); } 5)/arch/arm/plat-s3c24xx/irq.c void __init s3c24xx_init_irq(void) 6)/arch/arm/plat-s3c24xx/irq.c set_irq_handler(irqno, handle_edge_irq); 7)/kernel/irq/chip.c 调用4) 调用3) 8)在s3c24xx_init_irq这个函数里面,初始化irq_desc结构体 struct irq_desc { irq_flow_handler_t handle_irq; //set_irq_handler(irqno, handle_edge_irq); struct irq_chip *chip; //set_irq_chip(irqno, &s3c_irq_eint0t4); struct msi_desc *msi_desc; void *handler_data; void *chip_data; struct irqaction *action; /* IRQ action list */ unsigned int status; /* IRQ status */ .... 9)/kernel/irq/chip.c 分析handle_edge_irq: void fastcall handle_edge_irq(unsigned int irq, struct irq_desc *desc) { const unsigned int cpu = smp_processor_id(); spin_lock(&desc->lock); desc->status &= ~(IRQ_REPLAY | IRQ_WAITING); /* * If we're currently running this IRQ, or its disabled, * we shouldn't process the IRQ. Mark it pending, handle * the necessary masking and go out */ if (unlikely((desc->status & (IRQ_INPROGRESS | IRQ_DISABLED)) || !desc->action)) { desc->status |= (IRQ_PENDING | IRQ_MASKED); mask_ack_irq(desc, irq); goto out_unlock; } kstat_cpu(cpu).irqs[irq]++; //发生中断的次数 /* Start handling the irq */ //开始处理中断 desc->chip->ack(irq); //desc->chip chip是之前初始化的s3c_irqext_chip /* Mark the IRQ currently in progress.*/ desc->status |= IRQ_INPROGRESS; do { struct irqaction *action = desc->action; irqreturn_t action_ret; if (unlikely(!action)) { //判断链表是否为空 desc->chip->mask(irq); goto out_unlock; } /* * When another irq arrived while we were handling * one, we could have masked the irq. * Renable it, if it was not disabled in meantime. */ if (unlikely((desc->status & (IRQ_PENDING | IRQ_MASKED | IRQ_DISABLED)) == (IRQ_PENDING | IRQ_MASKED))) { desc->chip->unmask(irq); desc->status &= ~IRQ_MASKED; } desc->status &= ~IRQ_PENDING; spin_unlock(&desc->lock); action_ret = handle_IRQ_event(irq, action);//真正的处理过程 if (!noirqdebug) note_interrupt(irq, desc, action_ret); ...... } 10) /arch/arm/plat-s3c24xx/irq.c s3c_irqext_chip中的.ack static struct irq_chip s3c_irqext_chip = { .name = "s3c-ext", .mask = s3c_irqext_mask, .unmask = s3c_irqext_unmask, .ack = s3c_irqext_ack, .set_type = s3c_irqext_type, .set_wake = s3c_irqext_wake }; s3c_irqext_ack分析: static void s3c_irqext_ack(unsigned int irqno) { unsigned long req; unsigned long bit; unsigned long mask; bit = 1UL << (irqno - EXTINT_OFF); mask = __raw_readl(S3C24XX_EINTMASK); __raw_writel(bit, S3C24XX_EINTPEND); req = __raw_readl(S3C24XX_EINTPEND); req &= ~mask; /* not sure if we should be acking the parent irq... */ if (irqno <= IRQ_EINT7 ) { if ((req & 0xf0) == 0) s3c_irq_ack(IRQ_EINT4t7); } else { if ((req >> 8) == 0) s3c_irq_ack(IRQ_EINT8t23); } } static inline void s3c_irq_ack(unsigned int irqno) { unsigned long bitval = 1UL << (irqno - IRQ_EINT0); __raw_writel(bitval, S3C2410_SRCPND); __raw_writel(bitval, S3C2410_INTPND); } ①desc->chip->ack(irq);//上面是清理中断 ============================================================ ②handle_edge_irq取出action链表中的成员,执行action->handler irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action) { irqreturn_t ret, retval = IRQ_NONE; unsigned int status = 0; handle_dynamic_tick(action); if (!(action->flags & IRQF_DISABLED)) local_irq_enable_in_hardirq(); do { ret = action->handler(irq, action->dev_id); if (ret == IRQ_HANDLED) status |= action->flags; retval |= ret; action = action->next; } while (action); if (status & IRQF_SAMPLE_RANDOM) add_interrupt_randomness(irq); local_irq_disable(); return retval; } 总结: 按下按键 1>cup进入异常处理模式 b vector_irq + ... 2>__irq_user 3>asm_do_IRQ 4>irq_des[irq]以中断号为下标取出一项 ->handle_irq struct irq_desc { irq_flow_handler_t handle_irq; //set_irq_handler(irqno, handle_edge_irq); //handle_edge_irq取出action链表中的成员, //执行action->handler(自定义实现) struct irq_chip *chip; //set_irq_chip(irqno, &s3c_irq_eint0t4); //芯片相关的一些操作 ... } 5>handle_irq = handle_edge_irq 6>handle_edge_irq的操作: (1)desc->chip->ack(irq); (2)handle_IRQ_event(irq, action); =========================================================================================== 自定义异常处理函数action->handler,注册进内核 request_irq 1./kernel/irq/manage.c int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id) { //1)分配了一个结构,结构中的成员指向传递进来的参数 action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC); //2)设置irq retval = setup_irq(irq, action); } 2.setup_irq()函数分析: 1)irq_des[irq] 已irq为下标找到数组项 2)在irq_des[irq]链表里面加入传递进来的参数action 3)desc->chip->settype()设置为中断引脚 4)desc->chip->startup / desc->chip->enable 使能中断 free_irq(irq, dev_id) 1)从链表中除去 2)禁止中断