字符设备驱动(三)中断框架

那年仲夏 提交于 2020-01-27 07:53:57

title: 字符设备驱动(三)中断框架
tags: linux
date: 2018-11-22 18:58:22
toc: true
---

字符设备驱动(三)中断框架

引入

裸机中断流程

  1. 外部触发
  2. CPU 发生中断, 强制的跳到异常向量处
  3. 跳转到具体函数
    1. 保存被中断处的现场(各种寄存器的值)。
    2. 处理具体任务
    3. 恢复被中断的现场

LINUX流程

ARM 架构的 CPU 的异常向量基址可以是 0x0000 0000,也可以是 0xffff0000,这个地址并不代表实际的内存,是虚拟地址.当建立了虚拟地址与物理地址间的映射后,得将那些异常向量,即相当于把那些跳转指令复制拷贝到这个 0xffff0000 这个地址处去。

汇编处理

trap_init中实现(start_kernel中调用)

// arch/arm/kernel/traps.c
void __init trap_init(void)
{
    unsigned long vectors = CONFIG_VECTORS_BASE;
    extern char __stubs_start[], __stubs_end[];
    extern char __vectors_start[], __vectors_end[];
    extern char __kuser_helper_start[], __kuser_helper_end[];
    int kuser_sz = __kuser_helper_end - __kuser_helper_start;

    /*
     * Copy the vectors, stubs and kuser helpers (in entry-armv.S)
     * into the vector page, mapped at 0xffff0000, and ensure these
     * are visible to the instruction stream.
     */
    memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
    memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
    memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);

    /*
     * Copy signal return handlers into the vector page, and
     * set sigreturn to be a pointer to these.
     */
    memcpy((void *)KERN_SIGRETURN_CODE, sigreturn_codes,
           sizeof(sigreturn_codes));

    flush_icache_range(vectors, vectors + PAGE_SIZE);
    modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
}

拷贝向量表

memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
  1. vectors=CONFIG_VECTORS_BASE,是配置项,,在最开始的内核配置.config中定义为0xffff0000

  2. __vectors_startarch/arm/kernel/entry-armv.S中定义,是一段跳转指令,很明显是中断跳转指令

    .globl  __vectors_start
__vectors_start:
    swi SYS_ERROR0
    b   vector_und + stubs_offset
    ldr pc, .LCvswi + stubs_offset
    b   vector_pabt + stubs_offset
    b   vector_dabt + stubs_offset
    b   vector_addrexcptn + stubs_offset
    b   vector_irq + stubs_offset
    b   vector_fiq + stubs_offset

    .globl  __vectors_end
__vectors_end:

向量表宏解析

其中的vector_und等都是一个宏,搜索代码是搜索不到的.本质就是保护现场,设置管理模式,然后跳转

    .macro  vector_stub, name, mode, correction=0
    .align  5
    
vector_\name:
    .if \correction
    sub lr, lr, #\correction
    .endif

    @
    @ Save r0, lr_<exception> (parent PC) and spsr_<exception>
    @ (parent CPSR)
    @
    stmia   sp, {r0, lr}        @ save r0, lr
    mrs lr, spsr
    str lr, [sp, #8]        @ save spsr

    @
    @ Prepare for SVC32 mode.  IRQs remain disabled.
    @
    mrs r0, cpsr
    eor r0, r0, #(\mode ^ SVC_MODE)
    msr spsr_cxsf, r0

    @
    @ the branch table must immediately follow this code
    @
    and lr, lr, #0x0f
    mov r0, sp
    ldr lr, [pc, lr, lsl #2]
    movs    pc, lr          @ branch to handler in SVC mode
    .endm

尝试展开这个宏

    vector_stub und, UND_MODE

    .long   __und_usr           @  0 (USR_26 / USR_32)
    .long   __und_invalid           @  1 (FIQ_26 / FIQ_32)
    .long   __und_invalid           @  2 (IRQ_26 / IRQ_32)
    .long   __und_svc           @  3 (SVC_26 / SVC_32)
    .long   __und_invalid           @  4
    .long   __und_invalid           @  5
    .long   __und_invalid           @  6
    .long   __und_invalid           @  7
    .long   __und_invalid           @  8
    .long   __und_invalid           @  9
    .long   __und_invalid           @  a
    .long   __und_invalid           @  b
    .long   __und_invalid           @  c
    .long   __und_invalid           @  d
    .long   __und_invalid           @  e
    .long   __und_invalid           @  f

    .align  5

展开如下:

vector_und:

    @
    @ Save r0, lr_<exception> (parent PC) and spsr_<exception>
    @ (parent CPSR)
    @
    stmia   sp, {r0, lr}        @ save r0, lr
    mrs lr, spsr
    str lr, [sp, #8]        @ save spsr

    @
    @ Prepare for SVC32 mode.  IRQs remain disabled.
    @
    mrs r0, cpsr
    eor r0, r0, #(\mode ^ SVC_MODE)
    msr spsr_cxsf, r0

    @
    @ the branch table must immediately follow this code
    @
    and lr, lr, #0x0f
    mov r0, sp
    ldr lr, [pc, lr, lsl #2]
    movs    pc, lr          @ branch to handler in SVC mode
    
    
    .long   __und_usr           @  0 (USR_26 / USR_32)
    .long   __und_invalid           @  1 (FIQ_26 / FIQ_32)
    .long   __und_invalid           @  2 (IRQ_26 / IRQ_32)
    .long   __und_svc           @  3 (SVC_26 / SVC_32)
    .long   __und_invalid           @  4
    .long   __und_invalid           @  5
    .long   __und_invalid           @  6
    .long   __und_invalid           @  7
    .long   __und_invalid           @  8
    .long   __und_invalid           @  9
    .long   __und_invalid           @  a
    .long   __und_invalid           @  b
    .long   __und_invalid           @  c
    .long   __und_invalid           @  d
    .long   __und_invalid           @  e
    .long   __und_invalid           @  f

    .align  5

展开下中断跳转的宏vector_irq,这里比未定义指令异常增加了先计算lr返回地址lr=lr-4

#   vector_stub irq, IRQ_MODE, 4
#   correction=4

vector_irq:
    @.if \correction
    @sub    lr, lr, #\correction
    @.endif
    if #4
    sub lr,lr,#4
    endif

    @
    @ Save r0, lr_<exception> (parent PC) and spsr_<exception>
    @ (parent CPSR)
    @
    stmia   sp, {r0, lr}        @ save r0, lr
    mrs lr, spsr
    str lr, [sp, #8]        @ save spsr

    @
    @ Prepare for SVC32 mode.  IRQs remain disabled.
    @
    mrs r0, cpsr
    eor r0, r0, #(\mode ^ SVC_MODE)
    msr spsr_cxsf, r0

    @
    @ the branch table must immediately follow this code
    @
    and lr, lr, #0x0f
    mov r0, sp
    ldr lr, [pc, lr, lsl #2]
    movs    pc, lr          @ branch to handler in SVC mode

# 跳转地址
    .long   __irq_usr           @  0  (USR_26 / USR_32)
    .long   __irq_invalid           @  1  (FIQ_26 / FIQ_32)
    .long   __irq_invalid           @  2  (IRQ_26 / IRQ_32)
    .long   __irq_svc           @  3  (SVC_26 / SVC_32)
    .long   __irq_invalid           @  4
    .long   __irq_invalid           @  5
    .long   __irq_invalid           @  6
    .long   __irq_invalid           @  7
    .long   __irq_invalid           @  8
    .long   __irq_invalid           @  9
    .long   __irq_invalid           @  a
    .long   __irq_invalid           @  b
    .long   __irq_invalid           @  c
    .long   __irq_invalid           @  d
    .long   __irq_invalid           @  e
    .long   __irq_invalid           @  f

跳转函数

宏的最后都有一个跳转,比如中段函数跳转到__irq_usr

__irq_usr:
    usr_entry

#ifdef CONFIG_TRACE_IRQFLAGS
    bl  trace_hardirqs_off
#endif
    get_thread_info tsk
#ifdef CONFIG_PREEMPT
    ldr r8, [tsk, #TI_PREEMPT]      @ get preempt count
    add r7, r8, #1          @ increment it
    str r7, [tsk, #TI_PREEMPT]
#endif

    irq_handler
#ifdef CONFIG_PREEMPT
    ldr r0, [tsk, #TI_PREEMPT]
    str r8, [tsk, #TI_PREEMPT]
    teq r0, r7
    strne   r0, [r0, -r0]
#endif
#ifdef CONFIG_TRACE_IRQFLAGS
    bl  trace_hardirqs_on
#endif

    mov why, #0
    b   ret_to_user

    .ltorg

    .align  5

其中usr_entry用于用户模式下发生中断时初始化中断处理堆栈,同时保存所有SVC态寄存器到堆栈。

    .macro  usr_entry
    sub sp, sp, #S_FRAME_SIZE
    stmib   sp, {r1 - r12}

    ldmia   r0, {r1 - r3}
    add r0, sp, #S_PC       @ here for interlock avoidance
    mov r4, #-1         @  ""  ""     ""        ""

    str r1, [sp]        @ save the "real" r0 copied
                    @ from the exception stack

#if __LINUX_ARM_ARCH__ < 6 && !defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG)
#ifndef CONFIG_MMU
#warning "NPTL on non MMU needs fixing"
#else
    @ make sure our user space atomic helper is aborted
    cmp r2, #TASK_SIZE
    bichs   r3, r3, #PSR_Z_BIT
#endif
#endif

    @
    @ We are now ready to fill in the remaining blanks on the stack:
    @
    @  r2 - lr_<exception>, already fixed up for correct return/restart
    @  r3 - spsr_<exception>
    @  r4 - orig_r0 (see pt_regs definition in ptrace.h)
    @
    @ Also, separately save sp_usr and lr_usr
    @
    stmia   r0, {r2 - r4}
    stmdb   r0, {sp, lr}^

    @
    @ Enable the alignment trap while in kernel mode
    @
    alignment_trap r0

    @
    @ Clear FP to mark the first stack frame
    @
    zero_fp
    .endm

irq_handler也是一个宏,最终会调用asm_do_IRQ,也就是我们的处理函数

 .macro  irq_handler
 get_irqnr_preamble r5, lr
1:  get_irqnr_and_base r0, r6, r5, lr
 movne   r1, sp
 @
 @ routine called with r0 = irq number, r1 = struct pt_regs *
 @
 adrne   lr, 1b
 bne asm_do_IRQ

C函数处理

asm_do_IRQ

汇编处理流程最终会进入asm_do_IRQ处理

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

irq_desc是中断函数处理的数组,最终处理在desc_handle_irq(irq, desc); desc是中断全局数组,irq中断号.

static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc)
{
    desc->handle_irq(irq, desc);
}

也就是说,irq_desc是一个按照中断号为索引的结构体数组,里面存储各种信息包含中断入口函数handle_irq

struct irq_desc {
    irq_flow_handler_t  handle_irq;
    struct irq_chip     *chip;
    struct msi_desc     *msi_desc;
    void            *handler_data;
    void            *chip_data;
    struct irqaction    *action;    /* IRQ action list */
    unsigned int        status;     /* IRQ status */

    unsigned int        depth;      /* nested irq disables */
    unsigned int        wake_depth; /* nested wake enables */
    unsigned int        irq_count;  /* For detecting broken IRQs */
    unsigned int        irqs_unhandled;
    spinlock_t      lock;
#ifdef CONFIG_SMP
    cpumask_t       affinity;
    unsigned int        cpu;
#endif
#if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE)
    cpumask_t       pending_mask;
#endif
#ifdef CONFIG_PROC_FS
    struct proc_dir_entry   *dir;
#endif
    const char      *name;
} ____cacheline_internodealigned_in_smp;

__set_irq_handler

那么执行函数handle_irq具体是指向了什么?kernel/irq/chip.c中设置的desc->handle_irq = handle;

void
__set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
          const char *name)
{
    struct irq_desc *desc;
    unsigned long flags;

    if (irq >= NR_IRQS) {
        printk(KERN_ERR
               "Trying to install type control for IRQ%d\n", irq);
        return;
    }

    desc = irq_desc + irq;

    if (!handle)
        handle = handle_bad_irq;
    else if (desc->chip == &no_irq_chip) {
        printk(KERN_WARNING "Trying to install %sinterrupt handler "
               "for IRQ%d\n", is_chained ? "chained " : "", irq);
        /*
         * Some ARM implementations install a handler for really dumb
         * interrupt hardware without setting an irq_chip. This worked
         * with the ARM no_irq_chip but the check in setup_irq would
         * prevent us to setup the interrupt at all. Switch it to
         * dummy_irq_chip for easy transition.
         */
        desc->chip = &dummy_irq_chip;
    }

    spin_lock_irqsave(&desc->lock, flags);

    /* Uninstall? */
    if (handle == handle_bad_irq) {
        if (desc->chip != &no_irq_chip)
            mask_ack_irq(desc, irq);
        desc->status |= IRQ_DISABLED;
        desc->depth = 1;
    }
    desc->handle_irq = handle;
    desc->name = name;

    if (handle != handle_bad_irq && is_chained) {
        desc->status &= ~IRQ_DISABLED;
        desc->status |= IRQ_NOREQUEST | IRQ_NOPROBE;
        desc->depth = 0;
        desc->chip->unmask(irq);
    }
    spin_unlock_irqrestore(&desc->lock, flags);
}

函数__set_irq_handlerset_irq_handler调用

static inline void
set_irq_handler(unsigned int irq, irq_flow_handler_t handle)
{
    __set_irq_handler(irq, handle, 0, NULL);
}

s3c24xx_init_irq

set_irq_handler被多处调用,比如在arch/arm/plat-s3c24xx/irq.c中的s3c24xx_init_irq

void __init s3c24xx_init_irq(void)


....
        case IRQ_EINT4t7:
        case IRQ_EINT8t23:
        case IRQ_UART0:
        case IRQ_UART1:
        case IRQ_UART2:
        case IRQ_ADCPARENT:
            set_irq_chip(irqno, &s3c_irq_level_chip);
            set_irq_handler(irqno, handle_level_irq);
            break;

        case IRQ_RESERVED6:
        case IRQ_RESERVED24:
            /* no IRQ here */
            break;

....
    set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint4t7);
    set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8);

    set_irq_chained_handler(IRQ_UART0, s3c_irq_demux_uart0);
    set_irq_chained_handler(IRQ_UART1, s3c_irq_demux_uart1);
    set_irq_chained_handler(IRQ_UART2, s3c_irq_demux_uart2);
    set_irq_chained_handler(IRQ_ADCPARENT, s3c_irq_demux_adc);

...
    for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) {
        irqdbf("registering irq %d (extended s3c irq)\n", irqno);
        set_irq_chip(irqno, &s3c_irqext_chip);
        set_irq_handler(irqno, handle_edge_irq); ..............设置中断handler
        set_irq_flags(irqno, IRQF_VALID);
    }

比如我们的外部中断0处理

#define IRQ_EINT0      S3C2410_IRQ(0)       /* 16 */
for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {
    irqdbf("registering irq %d (ext int)\n", irqno);
    set_irq_chip(irqno, &s3c_irq_eint0t4);
    set_irq_handler(irqno, handle_edge_irq);
    set_irq_flags(irqno, IRQF_VALID);
}

handle_edge_irq

也就是填充这个全局结构数组irq_descIRQ_EINT0项中的相关信息,这里就是填充数组16~19.也就是中断最终会调用注册的handle_irq.在中断0~3中,注册了函数handle_edge_irqhandle_irq,边沿中断处理

///kernel/irq/chip.c
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);

    /* 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);
        spin_lock(&desc->lock);

    } while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING);

    desc->status &= ~IRQ_INPROGRESS;
out_unlock:
    spin_unlock(&desc->lock);
}

desc->chip->ack(irq);是之前设置的set_irq_chip(irqno, &s3c_irq_eint0t4);查找结构s3c_irq_eint0t4,具体也就是清中断了

static struct irq_chip s3c_irq_eint0t4 = {
    .name       = "s3c-ext0",
    .ack        = s3c_irq_ack,
    .mask       = s3c_irq_mask,
    .unmask     = s3c_irq_unmask,
    .set_wake   = s3c_irq_wake,
    .set_type   = s3c_irqext_type,
};
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);
}
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);
}
extern void     __raw_writel(u32 b, volatile void __iomem *addr);

handle_IRQ_event

接下来重点函数handle_IRQ_event,链表操作,取出action的链表成员,执行action->handle

struct irqaction *action = desc->action;
action_ret = handle_IRQ_event(irq, action);

desc->action

接着去分析action这个链表,它是desc的一个成员

struct irq_desc {
    irq_flow_handler_t  handle_irq;
    struct irq_chip     *chip;   //底层芯片相关的函数,清中断,开关使能等
    struct irqaction    *action;    /* IRQ action list */
    ...
}    
struct irqaction {
    irq_handler_t handler;
    unsigned long flags;
    cpumask_t mask;
    const char *name;
    void *dev_id;
    struct irqaction *next;
    int irq;
    struct proc_dir_entry *dir;
};

request_irq

action函数链表由request_irq设置kernel/irq/manage.c,完成了中断引脚的设置使能等

irq:中断号
handler:处理函数
irqflags:上升沿触发,下降沿触发,边沿触发等。指定了快速中断或中断共享等中断处理属性.
*devname:中断名字。通常是设备驱动程序的名称。改值用在 /proc/interrupt 系统 (虚拟)
文件上,或内核发生中断错误时使用。
dev_id 可作为共享中断时的中断区别参数,也可以用来指定中断服务函数需要参考的数据地址。也用于卸载action
int request_irq(unsigned int irq, irq_handler_t handler,
        unsigned long irqflags, const char *devname, void *dev_id)
{
    struct irqaction *action;
    int retval;

#ifdef CONFIG_LOCKDEP
    /*
     * Lockdep wants atomic interrupt handlers:
     */
    irqflags |= IRQF_DISABLED;
#endif
    /*
     * Sanity-check: shared interrupts must pass in a real dev-ID,
     * otherwise we'll have trouble later trying to figure out
     * which interrupt is which (messes up the interrupt freeing
     * logic etc).
     */
    if ((irqflags & IRQF_SHARED) && !dev_id)
        return -EINVAL;
    if (irq >= NR_IRQS)
        return -EINVAL;
    if (irq_desc[irq].status & IRQ_NOREQUEST)
        return -EINVAL;
    if (!handler)
        return -EINVAL;

    action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);
    if (!action)
        return -ENOMEM;

    action->handler = handler;
    action->flags = irqflags;
    cpus_clear(action->mask);
    action->name = devname;
    action->next = NULL;
    action->dev_id = dev_id;

    select_smp_affinity(irq);

#ifdef CONFIG_DEBUG_SHIRQ
    if (irqflags & IRQF_SHARED) {
        /*
         * It's a shared IRQ -- the driver ought to be prepared for it
         * to happen immediately, so let's make sure....
         * We do this before actually registering it, to make sure that
         * a 'real' IRQ doesn't run in parallel with our fake
         */
        if (irqflags & IRQF_DISABLED) {
            unsigned long flags;

            local_irq_save(flags);
            handler(irq, dev_id);
            local_irq_restore(flags);
        } else
            handler(irq, dev_id);
    }
#endif

    retval = setup_irq(irq, action);
    if (retval)
        kfree(action);

    return retval;
}

这里分配一个action成员,将传递的参数赋值到action的成员然后使用setup_irq(irq, action);设置

setup_irq

判断是否为共享中断,所谓共享中断也就是中断源一样的中断,共享中断只能加入共享中断的链表中.接着设置引脚等

int setup_irq(unsigned int irq, struct irqaction *new)
{
    struct irq_desc *desc = irq_desc + irq;
    struct irqaction *old, **p;
    const char *old_name = NULL;
    unsigned long flags;
    int shared = 0;

    if (irq >= NR_IRQS)
        return -EINVAL;

    if (desc->chip == &no_irq_chip)
        return -ENOSYS;
    /*
     * Some drivers like serial.c use request_irq() heavily,
     * so we have to be careful not to interfere with a
     * running system.
     */
    if (new->flags & IRQF_SAMPLE_RANDOM) {
        /*
         * This function might sleep, we want to call it first,
         * outside of the atomic block.
         * Yes, this might clear the entropy pool if the wrong
         * driver is attempted to be loaded, without actually
         * installing a new handler, but is this really a problem,
         * only the sysadmin is able to do this.
         */
        rand_initialize_irq(irq);
    }

    /*
     * The following block of code has to be executed atomically
     */
    spin_lock_irqsave(&desc->lock, flags);
    p = &desc->action;
    old = *p;
    if (old) {
        /*
         * Can't share interrupts unless both agree to and are
         * the same type (level, edge, polarity). So both flag
         * fields must have IRQF_SHARED set and the bits which
         * set the trigger type must match.
         */
        if (!((old->flags & new->flags) & IRQF_SHARED) ||
            ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK)) {
            old_name = old->name;
            goto mismatch;
        }

#if defined(CONFIG_IRQ_PER_CPU)
        /* All handlers must agree on per-cpuness */
        if ((old->flags & IRQF_PERCPU) !=
            (new->flags & IRQF_PERCPU))
            goto mismatch;
#endif

        /* add new interrupt at end of irq queue */
        do {
            p = &old->next;
            old = *p;
        } while (old);
        shared = 1;
    }

    *p = new;

    /* Exclude IRQ from balancing */
    if (new->flags & IRQF_NOBALANCING)
        desc->status |= IRQ_NO_BALANCING;

    if (!shared) {
        irq_chip_set_defaults(desc->chip);

#if defined(CONFIG_IRQ_PER_CPU)
        if (new->flags & IRQF_PERCPU)
            desc->status |= IRQ_PER_CPU;
#endif

        /* Setup the type (level, edge polarity) if configured: */
        if (new->flags & IRQF_TRIGGER_MASK) {
            if (desc->chip && desc->chip->set_type)
                desc->chip->set_type(irq,
                        new->flags & IRQF_TRIGGER_MASK);
            else
                /*
                 * IRQF_TRIGGER_* but the PIC does not support
                 * multiple flow-types?
                 */
                printk(KERN_WARNING "No IRQF_TRIGGER set_type "
                       "function for IRQ %d (%s)\n", irq,
                       desc->chip ? desc->chip->name :
                       "unknown");
        } else
            compat_irq_chip_set_default_handler(desc);

        desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING |
                  IRQ_INPROGRESS);

        if (!(desc->status & IRQ_NOAUTOEN)) {
            desc->depth = 0;
            desc->status &= ~IRQ_DISABLED;
            if (desc->chip->startup)
                desc->chip->startup(irq);
            else
                desc->chip->enable(irq);
        } else
            /* Undo nested disables: */
            desc->depth = 1;
    }
    /* Reset broken irq detection when installing new handler */
    desc->irq_count = 0;
    desc->irqs_unhandled = 0;
    spin_unlock_irqrestore(&desc->lock, flags);

    new->irq = irq;
    register_irq_proc(irq);
    new->dir = NULL;
    register_handler_proc(irq, new);

    return 0;

mismatch:
#ifdef CONFIG_DEBUG_SHIRQ
    if (!(new->flags & IRQF_PROBE_SHARED)) {
        printk(KERN_ERR "IRQ handler type mismatch for IRQ %d\n", irq);
        if (old_name)
            printk(KERN_ERR "current handler: %s\n", old_name);
        dump_stack();
    }
#endif
    spin_unlock_irqrestore(&desc->lock, flags);
    return -EBUSY;
}

这里desc->chip->set_type调用设置中断的引脚寄存器等,是在初始化中s3c24xx_init_irq赋值过的chip,通过request_irq传递的flag指定触发模式等

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

static struct irq_chip s3c_irq_eint0t4 = {
    .name       = "s3c-ext0",
    .ack        = s3c_irq_ack,
    .mask       = s3c_irq_mask,
    .unmask     = s3c_irq_unmask,
    .set_wake   = s3c_irq_wake,
    .set_type   = s3c_irqext_type,
};

int
s3c_irqext_type(unsigned int irq, unsigned int type)
{
    void __iomem *extint_reg;
    void __iomem *gpcon_reg;
    unsigned long gpcon_offset, extint_offset;
    unsigned long newvalue = 0, value;

    if ((irq >= IRQ_EINT0) && (irq <= IRQ_EINT3))
    {
        gpcon_reg = S3C2410_GPFCON;
        extint_reg = S3C24XX_EXTINT0;
        gpcon_offset = (irq - IRQ_EINT0) * 2;
        extint_offset = (irq - IRQ_EINT0) * 4;
    }
    else if ((irq >= IRQ_EINT4) && (irq <= IRQ_EINT7))
    {
        gpcon_reg = S3C2410_GPFCON;
        extint_reg = S3C24XX_EXTINT0;
        gpcon_offset = (irq - (EXTINT_OFF)) * 2;
        extint_offset = (irq - (EXTINT_OFF)) * 4;
    }
    else if ((irq >= IRQ_EINT8) && (irq <= IRQ_EINT15))
    {
        gpcon_reg = S3C2410_GPGCON;
        extint_reg = S3C24XX_EXTINT1;
        gpcon_offset = (irq - IRQ_EINT8) * 2;
        extint_offset = (irq - IRQ_EINT8) * 4;
    }
    else if ((irq >= IRQ_EINT16) && (irq <= IRQ_EINT23))
    {
        gpcon_reg = S3C2410_GPGCON;
        extint_reg = S3C24XX_EXTINT2;
        gpcon_offset = (irq - IRQ_EINT8) * 2;
        extint_offset = (irq - IRQ_EINT16) * 4;
    } else
        return -1;

    /* Set the GPIO to external interrupt mode */
    value = __raw_readl(gpcon_reg);
    value = (value & ~(3 << gpcon_offset)) | (0x02 << gpcon_offset);
    __raw_writel(value, gpcon_reg);

    /* Set the external interrupt to pointed trigger type */
    switch (type)
    {
        case IRQT_NOEDGE:
            printk(KERN_WARNING "No edge setting!\n");
            break;

        case IRQT_RISING:
            newvalue = S3C2410_EXTINT_RISEEDGE;
            break;

        case IRQT_FALLING:
            newvalue = S3C2410_EXTINT_FALLEDGE;
            break;

        case IRQT_BOTHEDGE:
            newvalue = S3C2410_EXTINT_BOTHEDGE;
            break;

        case IRQT_LOW:
            newvalue = S3C2410_EXTINT_LOWLEV;
            break;

        case IRQT_HIGH:
            newvalue = S3C2410_EXTINT_HILEV;
            break;

        default:
            printk(KERN_ERR "No such irq type %d", type);
            return -1;
    }

    value = __raw_readl(extint_reg);
    value = (value & ~(7 << extint_offset)) | (newvalue << extint_offset);
    __raw_writel(value, extint_reg);

    return 0;
}

free_irq

关闭中断,也就是取消中断处理函数,在全局数组irq_desc寻找到对应的action链表,通过id将其从链表中删除.如果链表空了,则禁止中断

void free_irq(unsigned int irq, void *dev_id)

小结

流程

  1. start_kernel中调用trap_init 拷贝向量表到 0xffff0000
  2. 触发中断,进入中断向量表
  3. vector_irq跳转进入保存现场,跳转到__irq_usr
  4. __irq_usr调用usr_entry进行用户模式下发生中断时初始化中断处理堆栈,通过irq_handler调用asm_do_IRQ进入C函数处理
  5. asm_do_IRQ寻找irq_desc 数组,调用desc->handle_irq来执行irq_desc数组中的入口函数handle_irq,这里外中断的话执行的是handle_edge_irq
  6. 中断入口函数清中断,执行action中的链表函数
  7. action中会判断是否为共享中断,设置中断类型,引脚寄存器等,同时开启中断
  8. 使用free_irq卸载掉链表中的函数,如果链表函数空了则关中断

设置irq_desc数组

  1. s3c24xx_init_irq中进行数组的初始化
  2. set_irq_chip初始化数组中的chip结构
  3. set_irq_handler设置中断入口函数handle_irq
  4. request_irq来创建action链表,分配空间,并加入链表
  5. struct irq_chip *chip; 这是底层芯片相关的函数,清中断,开关使能等

关键结构成员

struct irq_desc {
    irq_flow_handler_t  handle_irq; //中断入口函数,在外中断指向了 handle_edge_irq
    struct irq_chip     *chip;      //芯片底层处理函数,指向s3c_irqex_chip
    struct irqaction    *action;    //具体执行函数的链表,会被handle_irq 入口函数使用的
}


struct irq_chip {
    const char  *name;
    unsigned int    (*startup)(unsigned int irq);   //启动中断
    void        (*shutdown)(unsigned int irq);      //关闭中断
    void        (*enable)(unsigned int irq);        //使能中断
    void        (*disable)(unsigned int irq);       //禁止中断

    void        (*ack)(unsigned int irq);           //响应中断,清中断
}

struct irqaction {
    irq_handler_t handler;                          //action的执行函数
    unsigned long flags;                            //标志,用于设置中断边沿等
};

完整框架图

流程一览

设置向量表

设置全局中断数组

设置用户action动作

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!