прерывание управления Linux (с)

Во-первых, процесс прерывания

1, происходит прерывание

Когда происходит прерывание, процессор будет переходить к вектору исключения таблице, соответствующему прерыванию обработки
векторных исключений таблицы в 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
рекомендация