linux kernel 之中断框架与机制解剖(s3c6410)

/**

  • struct irq_desc - interrupt descriptor
  • @irq: interrupt number for this descriptor
  • @handle_irq: highlevel irq-events handler [if NULL, __do_IRQ()]
  • @chip: low level interrupt hardware access
  • @msi_desc: MSI descriptor
  • @handler_data: per-IRQ data for the irq_chip methods
  • @chip_data: platform-specific per-chip private data for the chip
  • methods, to allow shared chip implementations
  • @action: the irq action chain
  • @status: status information
  • @depth: disable-depth, for nested irq_disable() calls
  • @wake_depth: enable depth, for multiple set_irq_wake() callers
  • @irq_count: stats field to detect stalled irqs
  • @irqs_unhandled: stats field for spurious unhandled interrupts
  • @last_unhandled: aging timer for unhandled count
  • @lock: locking for SMP
  • @affinity: IRQ affinity on SMP
  • @cpu: cpu index useful for balancing
  • @pending_mask: pending rebalanced interrupts
  • @dir: /proc/irq/ procfs entry
  • @name: flow handler name for /proc/interrupts output
    */
    <include/linux/irq.h>
    struct irq_desc {
    unsigned int irq;

//当前中断处理函数入口,是一个函数指针。
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;
unsigned long last_unhandled; /
Aging timer for unhandled count */
spinlock_t lock;
#ifdef CONFIG_SMP
cpumask_t affinity;
unsigned int cpu;
#endif
#ifdef CONFIG_GENERIC_PENDING_IRQ
cpumask_t pending_mask;
#endif
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
const char *name;
} ____cacheline_internodealigned_in_smp;

extern struct irq_desc irq_desc[NR_IRQS];

///
尤entry_armv.s 里面的中断底层汇编可知asm_do_IRQ函数式中断的C语言总入口函数。它在arch/arm/kernel/irq.c中定义

           /**
  • 我们暂且可以认为,绝大部分中断是从汇编跳到本函数处理的。当然,IPI和local_timer不是。

  •             irq:            产生中断的外部中断号。
    
  •              regs:                  被中断打断的寄存器现场。
    

*/
asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{

       /**

 * 将当前正在处理的中断现场保存到每CPU变量__irq_regs中去。

 * 这样做的目的,是为了在其他代码中,直接读取__irq_regs中的值,找到中断前的现场。

 * 而不用将regs参数层层传递下去。

 */

struct pt_regs old_regs = set_irq_regs(regs);
irq_enter();
/

  • Some hardware gives randomly wrong interrupts. Rather
  • than crashing, do something sensible.
    /
    if (irq >= NR_IRQS) //中断号错误,超过最大中断号
    handle_bad_irq(irq, &bad_irq_desc);
    else
    generic_handle_irq(irq);///
    这里进行正常的中断处理 /
    /
    AT91 specific workaround */
    irq_finish(irq);
    irq_exit();
    //恢复__irq_regs每CPU变量的内容。
    set_irq_regs(old_regs);
    }

    include/asm-generic/irq_regs.h

//这个函数就是记录中断现场
static inline struct pt_regs *set_irq_regs(struct pt_regs *new_regs)
{
struct pt_regs *old_regs, **pp_regs = &__get_cpu_var(__irq_regs);

old_regs = *pp_regs;
*pp_regs = new_regs;
return old_regs;

}
///

           /**

 * 中断退出过程,主要处理以下内容:

 *              1、调试钩子,记录退出中断的事实。

 *              2、在任务的抢占计数字段中,递减中断计数

 *              3、处理软中断

 *              4、调用rcu模块的函数,表示已经退出中断。

 */

<kernel/softirq.c>
/*

  • Exit an interrupt context. Process softirqs if needed and possible:
    */
    void irq_exit(void)
    {
    account_system_vtime(current);
    trace_hardirq_exit();
    sub_preempt_count(IRQ_EXIT_OFFSET);
    if (!in_interrupt() && local_softirq_pending())
    invoke_softirq();

#ifdef CONFIG_NO_HZ
/* Make sure that timer wheel updates are propagated */
if (!in_interrupt() && idle_cpu(smp_processor_id()) && !need_resched())
tick_nohz_stop_sched_tick(0);
rcu_irq_exit();
#endif
preempt_enable_no_resched();
}
/

           /**

 * 调用irq_enter表示进入中断处理过程。该函数进行如下处理:

 *              1、rcu模块记录内部计数,表示当前退出了NOHZ状态。关于rcu,需要整整一本书来描述,请从http://xiebaoyou.download.csdn.net下载<深入理解并行编程>了解更多内容。

 *              2、处理NOHZ事件。

 *              3、将当前任务的抢占计数中的硬中断计数加1.该计数表示当前中断嵌套层次。

 *              4、调试信息,表示当前已经进入中断的事实。

 */

<kernel/softirq.c>
/*

  • Enter an interrupt context.
    */
    void irq_enter(void)
    {
    int cpu = smp_processor_id();

    if (idle_cpu(cpu) && !in_interrupt()) {
    __irq_enter();
    tick_check_idle(cpu);
    } else
    __irq_enter();
    }
    /
    <arch/arm/plat-s3c64xx/irq.c>
    static void s3c_irq_demux_timer0(unsigned int irq, struct irq_desc *desc)
    {
    s3c_irq_demux_timer(irq, IRQ_TIMER0);
    }
    //

<arch/arm/plat-s3c64xx/irq.c>
/* Timer interrupt handling */

static void s3c_irq_demux_timer(unsigned int base_irq, unsigned int sub_irq)
{
generic_handle_irq(sub_irq);
}
//
<include/linux/irq.h>中定义
static inline void generic_handle_irq(unsigned int irq)
{
// * 根据中断描述符中的信息,得到该中断ISR,并处理中断。

generic_handle_irq_desc(irq, irq_to_desc(irq));
}

/*

  • Architectures call this to let the generic IRQ layer
  • handle an interrupt. If the descriptor is attached to an
  • irqchip-style controller then we call the ->handle_irq() handler,
  • and it calls __do_IRQ() if it’s attached to an irqtype-style controller.
    */

<include/linux/irq.h>中定义
static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
{
#ifdef CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ
desc->handle_irq(irq, desc);
#else
if (likely(desc->handle_irq))
desc->handle_irq(irq, desc);
//此处调用s3c24xx_init_irq()中 set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8)的s3c_irq_demux_extint8()
else
__do_IRQ(irq);
#endif
}
///
handle.c
unsigned int __do_IRQ(unsigned int irq)
{
struct irq_desc *desc = irq_to_desc(irq);
struct irqaction action;
unsigned int status;
spin_lock(&desc->lock);
/

  • REPLAY is when Linux resends an IRQ that was dropped earlier
  • WAITING is used by probe to mark irqs that are being tested
    /
    status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING);
    status |= IRQ_PENDING; /
    we want to handle it /
    /
  • If the IRQ is disabled for whatever reason, we cannot
  • use the action we have.
    /
    action = NULL;
    if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) {
    action = desc->action;
    status &= ~IRQ_PENDING; /
    we commit to handling /
    status |= IRQ_INPROGRESS; /
    we are handling it /
    }
    desc->status = status;
    /
  • If there is no IRQ handler or it was disabled, exit early.
  • Since we set PENDING, if another processor is handling
  • a different instance of this same irq, the other processor
  • will take care of it.
    /
    if (unlikely(!action))
    goto out;
    /
  • Edge triggered interrupts need to remember
  • pending events.
  • This applies to any hw interrupts that allow a second
  • instance of the same irq to arrive while we are in do_IRQ
  • or in the handler. But the code here only handles the second
  • instance of the irq, not the third or fourth. So it is mostly
  • useful for irq hardware that does not mask cleanly in an
  • SMP environment.
    /
    for (;😉 {
    irqreturn_t action_ret;
    spin_unlock(&desc->lock);
    action_ret = handle_IRQ_event(irq, action);//通过此函数逐个执行action链表中用户注册的中断处理函数
    if (!noirqdebug)
    note_interrupt(irq, desc, action_ret);
    spin_lock(&desc->lock);
    if (likely(!(desc->status & IRQ_PENDING)))
    break;
    desc->status &= ~IRQ_PENDING;
    }
    desc->status &= ~IRQ_INPROGRESS;
    out:
    /
  • The ->end() handler has to deal with interrupts which got
  • disabled while the handler was running.
    /
    desc->chip->end(irq);
    spin_unlock(&desc->lock);
    return 1;
    }
    /
    handle.c
    /
    *
  • handle_IRQ_event - irq action chain handler
  • @irq: the interrupt number
  • @action: the interrupt action chain for this irq
  • Handles the action chain of an irq event
    */
    irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
    {
    irqreturn_t ret, retval = IRQ_NONE;
    unsigned int status = 0;
    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;
    }
    /
    /init/main.c asmlinkage void __init start_kernel(void) —>调用 arch/arm/kernel/irq.c void __init init_IRQ(void)

void __init init_IRQ(void)
{
int irq;
for (irq = 0; irq < NR_IRQS; irq++)
irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE;
#ifdef CONFIG_SMP
bad_irq_desc.affinity = CPU_MASK_ALL;
bad_irq_desc.cpu = smp_processor_id();
#endif
init_arch_irq();// 即为 s3c6410_init_irq
//以下为注释
//set_up.c 中有setup_arch()中有init_arch_irq=mdesc->init_irq;对于S3C2410,S3C2440开发板,<arch/arm/mach-s3c6410/mach_smdk6410.c>中有
MACHINE_START(SMDK6410, “SMDK6410”)
/* Maintainer: Ben Dooks ben@fluff.org */
.phys_io = S3C_PA_UART & 0xfff00000,
.io_pg_offst = (((u32)S3C_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C64XX_PA_SDRAM + 0x100,

.init_irq    = s3c6410_init_irq,
.map_io        = smdk6410_map_io,
.init_machine    = smdk6410_machine_init,
.timer        = &s3c64xx_timer,

MACHINE_END
//以上为注释
}
//
<arch/arm/mach-s3c6410/cpu.c>
void __init s3c6410_init_irq(void)
{
/* VIC0 is missing IRQ7, VIC1 is fully populated. */
s3c64xx_init_irq(~0 & ~(1 << 7), ~0);
}
//
<arch/arm/plat-s3c64xx/irq.c>
void __init s3c64xx_init_irq(u32 vic0_valid, u32 vic1_valid)
{
int uart, irq;

printk(KERN_DEBUG "%s: initialising interrupts\n", __func__);

/* initialise the pair of VICs */
vic_init(S3C_VA_VIC0, S3C_VIC0_BASE, vic0_valid);
vic_init(S3C_VA_VIC1, S3C_VIC1_BASE, vic1_valid);

/* add the timer sub-irqs */

set_irq_chained_handler(IRQ_TIMER0_VIC, s3c_irq_demux_timer0);
set_irq_chained_handler(IRQ_TIMER1_VIC, s3c_irq_demux_timer1);
set_irq_chained_handler(IRQ_TIMER2_VIC, s3c_irq_demux_timer2);
set_irq_chained_handler(IRQ_TIMER3_VIC, s3c_irq_demux_timer3);
set_irq_chained_handler(IRQ_TIMER4_VIC, s3c_irq_demux_timer4);

for (irq = IRQ_TIMER0; irq <= IRQ_TIMER4; irq++) {
    set_irq_chip(irq, &s3c_irq_timer);
    set_irq_handler(irq, handle_level_irq);
    set_irq_flags(irq, IRQF_VALID);
}

for (uart = 0; uart < ARRAY_SIZE(uart_irqs); uart++)
    s3c64xx_uart_irq(&uart_irqs[uart]);

}
///
<arch/arm/plat-s3c64xx/irq.c>

/* Note, we make use of the fact that the parent IRQs, IRQ_UART[0…3]

  • are consecutive when looking up the interrupt in the demux routines.
    */
    static struct uart_irq uart_irqs[] = {
    [0] = {
    .regs = S3C_VA_UART0,
    .base_irq = IRQ_S3CUART_BASE0,
    .parent_irq = IRQ_UART0,
    },
    [1] = {
    .regs = S3C_VA_UART1,
    .base_irq = IRQ_S3CUART_BASE1,
    .parent_irq = IRQ_UART1,
    },
    [2] = {
    .regs = S3C_VA_UART2,
    .base_irq = IRQ_S3CUART_BASE2,
    .parent_irq = IRQ_UART2,
    },
    [3] = {
    .regs = S3C_VA_UART3,
    .base_irq = IRQ_S3CUART_BASE3,
    .parent_irq = IRQ_UART3,
    },
    };
    //
    <arch/arm/plat-s3c64xx/irq.c>
    static void __init s3c64xx_uart_irq(struct uart_irq *uirq)
    {
    void *reg_base = uirq->regs;
    unsigned int irq;
    int offs;

    /* mask all interrupts at the start. */
    __raw_writel(0xf, reg_base + S3C64XX_UINTM);

    for (offs = 0; offs < 3; offs++) {
    irq = uirq->base_irq + offs;

     set_irq_chip(irq, &s3c_irq_uart);
     set_irq_chip_data(irq, uirq);
     set_irq_handler(irq, handle_level_irq);
     set_irq_flags(irq, IRQF_VALID);
    

    }

    set_irq_chained_handler(uirq->parent_irq, s3c_irq_demux_uart);
    }

    include/linux/irq.h中
    static inline void
    set_irq_chained_handler(unsigned int irq,
    irq_flow_handler_t handle)
    {
    __set_irq_handler(irq, handle, 1, NULL);
    }
    //
    kernel/irq/chip.c
    void __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
    const char *name)
    {
    struct irq_desc *desc = irq_to_desc(irq);
    unsigned long flags;
    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->startup(irq);
    }
    spin_unlock_irqrestore(&desc->lock, flags);
    }
    //
    static inline struct irq_desc *irq_to_desc(unsigned int irq)
    {
    return (irq < nr_irqs) ? irq_desc + irq : NULL;
    }
    ///
    irq.h中
    static inline struct irq_desc *irq_to_desc(unsigned int irq)
    {
    return (irq < nr_irqs) ? irq_desc + irq : NULL;
    }
    //

///
linux3.08内核的struct irqaction解构体
/**

  • struct irqaction - per interrupt action descriptor
  • @handler: interrupt handler function
  • @flags: flags (see IRQF_* above)
  • @name: name of the device
  • @dev_id: cookie to identify the device
  • @next: pointer to the next irqaction for shared interrupts
  • @irq: interrupt number
  • @dir: pointer to the proc/irq/NN/name entry
  • @thread_fn: interrupt handler function for threaded interrupts
  • @thread: thread pointer for threaded interrupts
  • @thread_flags: flags related to @thread
  • @thread_mask: bitmask for keeping track of @thread activity
    */
    //与request_irq函数对应的解构体
    struct irqaction {

//用户注册的中断处理函数,由我们自己来写的
irq_handler_t handler;

//触发中断方式
unsigned long flags;

//中断处理函数和卸载中断处理函数是用到
void *dev_id;
struct irqaction *next;

//中断号
int irq;
irq_handler_t thread_fn;
struct task_struct *thread;
unsigned long thread_flags;
unsigned long thread_mask;
const char *name;
struct proc_dir_entry *dir;
} ____cacheline_internodealigned_in_smp;

///
<include/linux/irqreturn.h>
中断处理函数的返回值
/**

  • enum irqreturn
  • @IRQ_NONE interrupt was not from this device 未处理,系统继续调用别的驱动函数去处理中断
  • @IRQ_HANDLED interrupt was handled by this device 已经被处理好了。
  • @IRQ_WAKE_THREAD handler requests to wake the handler thread
    */
    enum irqreturn {
    IRQ_NONE = (0 << 0),
    IRQ_HANDLED = (1 << 0),
    IRQ_WAKE_THREAD = (1 << 1),
    };

typedef enum irqreturn irqreturn_t;
#define IRQ_RETVAL(x) ((x) != IRQ_NONE)
/
用户注册中断处理函数: manage.c
/**

  • request_irq - allocate an interrupt line
  • @irq: Interrupt line to allocate
  • @handler: Function to be called when the IRQ occurs
  • @irqflags: Interrupt type flags
  • @devname: An ascii name for the claiming device 传递给request_irq的字符串,用来在/proc/interrupts中显示中断的拥有者
  • @dev_id: A cookie passed back to the handler function 用于共享的中断信号线
  • This call allocates interrupt resources and enables the
  • interrupt line and IRQ handling. From the point this
  • call is made your handler function may be invoked. Since
  • your handler function must clear any interrupt the board
  • raises, you must take care both to initialise your hardware
  • and to set up the interrupt handler in the right order.
  • Dev_id must be globally unique. Normally the address of the
  • device data structure is used as the cookie. Since the handler
  • receives this value it makes sense to use it.
  • If your interrupt is shared you must pass a non NULL dev_id
  • as this is required when freeing the interrupt.
  • Flags:
  • IRQF_SHARED Interrupt is shared
  • IRQF_DISABLED Disable local interrupts while processing
  • IRQF_SAMPLE_RANDOM The interrupt can be used for entropy
  • IRQF_TRIGGER_* Specify active edge(s) or level

*/在Linux内核中用于申请中断的函数是request_irq(),函数原型在Kernel/irq/manage.c中定义:
int request_irq(unsigned int irq, irq_handler_t handler,unsigned long irqflags, const char *devname, void *dev_id)
irq是要申请的硬件中断号。
handler是向系统注册的中断处理函数,是一个回调函数,中断发生时,系统调用这个函数,dev_id参数将被传递给它。
irqflags是中断处理的属性,若设置了IRQF_DISABLED (老版本中的SA_INTERRUPT,本版zhon已经不支持了),则表示中断处理程序是快速处理程序,快速处理程序被调用时屏蔽所有中断,慢速处理程序不屏蔽;若设置了IRQF_SHARED (老版本中的SA_SHIRQ),则表示多个设备共享中断,若设置了IRQF_SAMPLE_RANDOM(老版本中的SA_SAMPLE_RANDOM),表示对系统熵有贡献,对系统获取随机数有好处。(这几个flag是可以通过或的方式同时使用的)
devname设置中断名称,通常是设备驱动程序的名称 在cat /proc/interrupts中可以看到此名称。
dev_id在中断共享时会用到,一般设置为这个设备的设备结构体或者NULL。

request_irq()返回0表示成功,返回-INVAL表示中断号无效或处理函数指针为NULL,返回-EBUSY表示中断已经被占用且不能共享。
<kernel/irq/manage.c>
int request_irq(unsigned int irq, irq_handler_t handler,
unsigned long irqflags, const char *devname, void *dev_id)
{
struct irqaction *action;
struct irq_desc desc;
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;
    desc = irq_to_desc(irq); //根据中断号来获得关于中断资源的描述
    if (desc->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;
    __setup_irq函数,该函数才是真正的将中断注册
    retval = __setup_irq(irq, desc, action);
    if (retval)
    kfree(action);
    #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 disable the irq to make sure that a ‘real’ IRQ doesn’t
  • run in parallel with our fake.
    */
    unsigned long flags;
    disable_irq(irq);
    local_irq_save(flags);
    handler(irq, dev_id); //???为啥在这里立即调用了中断的handler函数(这是一个参数) 看看宏CONFIG_DEBUG_SHIRQ
    local_irq_restore(flags);
    enable_irq(irq);
    }
    #endif
    return retval;
    }
    /
    manage.c中

__setup_irq函数,该函数才是真正的将中断注册 /*

  • Internal function to register an irqaction - typically used to
  • allocate special interrupts that are part of the architecture.
    */
    static int
    __setup_irq(unsigned int irq, struct irq_desc * desc, struct irqaction *new)
    {
    struct irqaction *old, **p;
    const char old_name = NULL;
    unsigned long flags;
    int shared = 0;
    int ret;
    /
  • The following block of code has to be executed atomically
    */
    spin_lock_irqsave(&desc->lock, flags);
    p = &desc->action;
    old = p;
    if (old) {
    /
    add new interrupt at end of irq queue */ ????????????????????????????、
    do {
    p = &old->next;
    old = p;
    } while (old);
    shared = 1;
    }
    if (!shared) {
    irq_chip_set_defaults(desc->chip);
    /
    Setup the type (level, edge polarity) if configured: /
    if (new->flags & IRQF_TRIGGER_MASK) {
    ret = __irq_set_trigger(desc, irq, new->flags);
    if (ret) {
    spin_unlock_irqrestore(&desc->lock, flags);
    return ret;
    }
    } else
    compat_irq_chip_set_default_handler(desc);
    desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING |
    IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED);
    if (!(desc->status & IRQ_NOAUTOEN)) {
    desc->depth = 0;
    desc->status &= ~IRQ_DISABLED;
    desc->chip->startup(irq);
    } else
    /
    Undo nested disables: /
    desc->depth = 1;
    /
    Exclude IRQ from balancing if requested /
    if (new->flags & IRQF_NOBALANCING)
    desc->status |= IRQ_NO_BALANCING;
    /
    Set default affinity mask once everything is setup /
    do_irq_select_affinity(irq, desc);
    } else if ((new->flags & IRQF_TRIGGER_MASK)
    && (new->flags & IRQF_TRIGGER_MASK)
    != (desc->status & IRQ_TYPE_SENSE_MASK)) {
    /
    hope the handler works with the actual trigger mode… */
    pr_warning(“IRQ %d uses trigger mode %d; requested %d\n”,
    irq, (int)(desc->status & IRQ_TYPE_SENSE_MASK),
    (int)(new->flags & IRQF_TRIGGER_MASK));
    }
    p = new;
    /
    Reset broken irq detection when installing new handler /
    desc->irq_count = 0;
    desc->irqs_unhandled = 0;
    /
  • Check whether we disabled the irq via the spurious handler
  • before. Reenable it and give it another chance.
    */
    if (shared && (desc->status & IRQ_SPURIOUS_DISABLED)) {
    desc->status &= ~IRQ_SPURIOUS_DISABLED;
    __enable_irq(desc, irq);
    }
    spin_unlock_irqrestore(&desc->lock, flags);
    new->irq = irq;
    register_irq_proc(irq, desc);
    new->dir = NULL;
    register_handler_proc(irq, new);
    return 0;
    mismatch:
    spin_unlock_irqrestore(&desc->lock, flags);
    return -EBUSY;
    }
    /
    arch/arm/kernel/setup.c void __init setup_arch(char **cmdline_p) 调用
    early_trap_init(void)
    arch/arm/kernel/traps.c

void __init early_trap_init(void)
{

// include/linux/autoconf.h #define CONFIG_VECTORS_BASE 0xffff0000
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.
 */

//将_vectors_start到__vectors_start之间的代码拷贝到0xffff0000

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

}

中断共享
如果开发板上按键的中断已经被另外的驱动程序注册中断了,而我现在又想再注册一次这个中断,这就出现了一个中断号不止对应一个中断函数的情况。注意,这里与硬件上的共享中断不一样,这里是指,当一个中断信号来了,基于操作系统,一个中断的到来可以调用多个中断处理程序,与硬件无关。

SA_SHIRQ:这个标志表明多个中断处理程序可以共享一个中断号。
图片

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xx-xx-xxx-xxx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值