Linuxの割り込み管理(C)

まず、割り込み処理

1、割り込みが発生します

割り込みが発生すると、CPUは対応する割り込み処理、例外処理ベクタテーブルにジャンプします
で例外処理ベクタテーブルをarch\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


         /* 中断入口地址。发生中断时会跳到这里,执行这句跳转语句,
          *+ stubs_offset 是因为异常向量表使用高地址0xffff0000,异常向量copy0xffff0000处 */
    b   vector_irq + stubs_offset   

 
    b   vector_fiq + stubs_offset

    .globl  __vectors_end
__vectors_end:

IRQ割り込みが発生した場合、それが実行されb vector_irq + stubs_offset、それは、次のコードブロックにジャンプします、この言語を
するvector_stubには、マクロアーキテクチャのLinuxを例外処理の簡単な紹介があります

    /* vector_stub 是汇编下定义的一个宏, 里面是一块代码块,主要用于计算返回地址,计算下一步要跳转的入口地址
    * irq这个宏的名字, 
    * #define IRQ_MODE 0x00000012  
    * 4 这个是用于计算返回地址的
    */
    vector_stub irq, IRQ_MODE, 4     
    /* 下面要跳转到那个入口,是在 vector_stub 宏里面计算并且跳转的*/
    .long   __irq_usr           @  0  (USR_26 / USR_32)    /* 用户模式下发生irq异常 */
    .long   __irq_invalid           @  1  (FIQ_26 / FIQ_32)
    .long   __irq_invalid           @  2  (IRQ_26 / IRQ_32)
    .long   __irq_svc           @  3  (SVC_26 / SVC_32)    /* 管理模式下发生irq异常 */
    .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

前に例外処理アーキテクチャのLinuxそれはユーザーモードの割り込みで発生した、または割り込みが管理モードの下で発生するかどうか、に導入、割り込みサービスルーチンは、(ちょうど必要性の前で)同じです。
その後に上の__irq_usr分析。

__irq_usr:
    /* usr_entry 也是一个宏,里面的代码块作用是保存 r0、r1 等一些寄存器 */
    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       /* 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

irq_handlerこのマクロは、で定義されているarch\arm\kernel\entry-armv.Sファイルの内部

    .macro  irq_handler
    get_irqnr_preamble r5, lr    /*get_irqnr_preamble 是一个宏,里面什么都没做 */
1:  get_irqnr_and_base r0, r6, r5, lr  /* get_irqnr_and_base 也是一个宏,在里面会计算当前的中断号(中断号通过 INTOFFSET 寄存器的某一位来确定)、中断状态等,和struct pt_regs 结构体压栈。*/
    movne   r1, sp    /* 通过堆栈将struct pt_regs* 传给 asm_do_IRQ 函数 */
    @
    @ routine called with r0 = irq number, r1 = struct pt_regs *
    @
    adrne   lr, 1b
    bne asm_do_IRQ   /* 到这里就是跳转过去执行中断服务函数了 */

2、割り込み処理

asm_do_IRQ()この関数は、すべての割り込みサービスの関数です。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);
    /* 通过中断号获取 irq_desc 数组项*/
    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);
}

ハンドラ割り込み登録ユーザを呼び出すことですdesc_handle_irq機能。desc_handle_irq関数でinclude\asm-arm\mach\Irq.h定義ファイルの内部。コードブロック、次の

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

*追加情報:

asm_do_IRQ範囲パラメータIRQ関数値があるIRQ_EINT0 ~ 31だけで32個の値。asm_do_IRQ関数のirqパラメータは、割り込みがチップS3C2440によって決定される割込み番号の組であってもよく、実際の割り込み番号であってもよい。後に割り込みが発生するINTPNDビットレジスタは1にセットされ、INTOFFSETレジスタの値が決定されirqたパラメータを。すべての実際の中断は、irq_descそのうちの数よりも多く、それに対応する配列を、持っています。場合asm_do_IRQ関数は、IRQ割り込み「のセット」表現され、irq_desc[irq].handle_irqメンバ関数は、最初の(割り込み数であると仮定すると割り込みこのグループの割り込みを区別する必要があるirqno)、次いでコールirq_desc[irqno].handle_irqさらなる処理のために。

外部割り込みEINT8〜EINT23例:

(1)割込みがトリガされ、INTOFFSETレジスタの値は、5であるasm_do_IRQ関数IRQ値のパラメータ(IRQ_EINT0+5)、すなわち、IRQ_EINT8t23その後、呼び出すirq_desc[IRQ_EINT8t23].handle_irqプロセスに機能を
(2)irq_desc[IRQ_EINT8t23].handle_irq初期化時に、それがに初期化されるs3c_irq_demux_extint8
(3)s3c_irq_demux_extint8関数arch\arm\plat-s3c24xx\Irq.c定義ファイルの内部。これは、最初の読み取りEINTPEND 和 EINTMASK割り込みが発生したかを決定するために、レジスタの値を、次に、割り込み番号を再計算し、次に呼び出しirq_descで配列項目をhandle_irqメンバ関数。

s3c_irq_demux_extint8 次のように機能コードは次のようになります。

static void
s3c_irq_demux_extint8(unsigned int irq, struct irq_desc *desc)
{    
    /* 读取 EINTPEND 寄存器的值,EINT8 ~ EINT23 发生时相应的位被置1 */
    unsigned long eintpnd = __raw_readl(S3C24XX_EINTPEND);
    /* 读取屏蔽寄存器的值 */
    unsigned long eintmsk = __raw_readl(S3C24XX_EINTMASK);
    /* 清除被屏蔽位 */
    eintpnd &= ~eintmsk;
    /* 清除低8位,EINT8对应位8 ... */
    eintpnd &= ~0xff;   /* ignore lower irqs */

    /* we may as well handle all the pending IRQs here */
    /* 循环处理所有的子中断 */
    while (eintpnd) {
        /* 确定 eintpnd 中为1的最高位  */
        irq = __ffs(eintpnd);
       /* 将该位清零 */
        eintpnd &= ~(1<<irq);
        /* 重新计算中断号:前面计算出,如果 irq = __ffs(eintpnd) 算出的是8,则中断号为8 */
            irq += (IRQ_EINT4 - 4);
        /* 调用这个中断真正的处理函数(handle_edge_irq函数)  */
        desc_handle_irq(irq, irq_desc + irq);
    }

}

(4)IRQ_EINT8 ~ IRQ_EINT23におけるこれらの実際の割り込みハンドラの入り口、init_IRQ初期化関数の時間は、割り込み初期化handle_edge_irq(エッジトリガ)機能。
handle_edge_irq機能kernel\irq\Chip.c次のように、定義したファイル:

void fastcall handle_edge_irq(unsigned int irq, struct irq_desc *desc)
{
    ...
    /* 统计中断发生的次数 */
    kstat_cpu(cpu).irqs[irq]++;

    /* Start handling the irq */
    /* 调用 chip 底层的操作函数 清除中断 */
    desc->chip->ack(irq);

    /* Mark the IRQ currently in progress.*/
    desc->status |= IRQ_INPROGRESS;

    do {
        /* 依次调用 action 链表里面的处理函数(用户注册的函数都是挂在action链表里面的) */
        action_ret = handle_IRQ_event(irq, action);
        

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

}

handle_IRQ_event順次呼び出しactionハンドラ内のリストを、この機能kernel\irq\Handle.cブロック以下、定義ファイル

irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
{

    
    ...
     /* 依次调用 action 链表的成员函数*/
    do {
        ret = action->handler(irq, action->dev_id);
        if (ret == IRQ_HANDLED)
            status |= action->flags;
        retval |= ret;
        action = action->next;
    } while (action);

    ...
    return retval;
}

注:上記の分析は、エッジトリガ呼び出しのためのプロセスは、エッジトリガー割り込みであるhandle_edge_irq()レベルトリガ、呼処理の関数であるhandle_level_irqプロセスは類似している方法の機能だけでなく、レベルトリガ割り込みこのシールドまた、割り込みを処理した後に開きます。

要約:

(1)割り込み、割り込みベクタコールasm_do_IRQ、着信割り込み番号irq

(2)asm_do_IRQ割り込み数に応じて機能をirq呼び出しますirq_desc[irq].handle_irq彼は、割り込みハンドラの入り口です。レベルトリガするためにこの関数は、通常、入口でhandle_level_irq通常エントリエッジトリガ機能のためhandle_edge_irq

(3)クリア割り込み関数の最初のエントリは、エントリ関数であるhandle_level_irqにも割り込みをマスクします。

(4)利用者ごとのコールに登録irq_desc[irq].action割り込みハンドラに登録リスト

(5)エントリ関数であるhandle_level_irq時間が、また、処理後に割り込みをオープンしました。

おすすめ

転載: www.cnblogs.com/gulan-zmc/p/12008047.html