1. 程式人生 > >字元裝置驅動(三)中斷框架

字元裝置驅動(三)中斷框架

目錄


title: 字元裝置驅動(三)中斷框架
tags: linux
date: 2018-11-22 18:58:22
toc: true
---

字元裝置驅動(三)中斷框架

引入

mark

裸機中斷流程

  1. 外部觸發
  2. CPU 發生中斷, 強制的跳到異常向量處
  3. 跳轉到具體函式
    1. 儲存被中斷處的現場(各種暫存器的值)。
    2. 處理具體任務
    3. 恢復被中斷的現場

LINUX流程

ARM 架構的 CPU 的異常向量基址可以是 0x0000 0000

,也可以是 0xffff0000,這個地址並不代表實際的記憶體,是虛擬地址.當建立了虛擬地址與實體地址間的對映後,得將那些異常向量,即相當於把那些跳轉指令複製拷貝到這個 0xffff0000 這個地址處去。

彙編處理

mark

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;

mark

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

mark

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;                            //標誌,用於設定中斷邊沿等
};

mark

完整框架圖

mark

流程一覽

mark

設定向量表

mark

設定全域性中斷陣列

mark

設定使用者action動作

mark