Linux中断(interrupt)子系统之二:arch相关的硬件封装层【转】

转自:http://blog.csdn.net/droidphone/article/details/7467436

版权声明:本文为博主原创文章,未经博主允许不得转载。

目录(?)[-]

  1. CPU的中断入口
  2. 初始化
  3. 中断控制器的软件抽象struct irq_chip
  4. 进入流控处理层
  5. 中断控制器的级联

Linux的通用中断子系统的一个设计原则就是把底层的硬件实现尽可能地隐藏起来,使得驱动程序的开发人员不用关注底层的实现,要实现这个目标,内核的开发者们必须把硬件相关的内容剥离出来,然后定义一些列标准的接口供上层访问,上层的开发人员只要知道这些接口即可完成对中断的进一步处理和控制。对底层的封装主要包括两部分:

  • 实现不同体系结构中断入口,这部分代码通常用asm实现;
  • 中断控制器进行封装和实现;

本文的内容正是要讨论硬件封装层的实现细节。我将以ARM体系进行介绍,大部分的代码位于内核代码树的arch/arm/目录内。

/*****************************************************************************************************/
声明:本博内容均由http://blog.csdn.net/droidphone原创,转载请注明出处,谢谢!
/*****************************************************************************************************/

1.  CPU的中断入口

我们知道,arm的异常和复位向量表有两种选择,一种是低端向量,向量地址位于0x00000000,另一种是高端向量,向量地址位于0xffff0000,Linux选择使用高端向量模式,也就是说,当异常发生时,CPU会把PC指针自动跳转到始于0xffff0000开始的某一个地址上:

ARM的异常向量表
地址 异常种类
FFFF0000 复位
FFFF0004 未定义指令
FFFF0008 软中断(swi)
FFFF000C Prefetch abort
FFFF0010 Data abort
FFFF0014 保留
FFFF0018 IRQ
FFFF001C FIQ

中断向量表在arch/arm/kernel/entry_armv.S中定义,为了方便讨论,下面只列出部分关键的代码:

[plain] view plain copy

  1. .globl  __stubs_start
  2. __stubs_start:
  3. vector_stub irq, IRQ_MODE, 4
  4. .long   __irq_usr           @  0  (USR_26 / USR_32)
  5. .long   __irq_invalid           @  1  (FIQ_26 / FIQ_32)
  6. .long   __irq_invalid           @  2  (IRQ_26 / IRQ_32)
  7. .long   __irq_svc           @  3  (SVC_26 / SVC_32)
  8. vector_stub dabt, ABT_MODE, 8
  9. .long   __dabt_usr          @  0  (USR_26 / USR_32)
  10. .long   __dabt_invalid          @  1  (FIQ_26 / FIQ_32)
  11. .long   __dabt_invalid          @  2  (IRQ_26 / IRQ_32)
  12. .long   __dabt_svc          @  3  (SVC_26 / SVC_32)
  13. vector_fiq:
  14. disable_fiq
  15. subs    pc, lr, #4
  16. ......
  17. .globl  __stubs_end
  18. __stubs_end:
  19. .equ    stubs_offset, __vectors_start + 0x200 - __stubs_start
  20. .globl  __vectors_start
  21. __vectors_start:
  22. ARM(   swi SYS_ERROR0  )
  23. THUMB( svc #0      )
  24. THUMB( nop         )
  25. W(b)    vector_und + stubs_offset
  26. W(ldr)  pc, .LCvswi + stubs_offset
  27. W(b)    vector_pabt + stubs_offset
  28. W(b)    vector_dabt + stubs_offset
  29. W(b)    vector_addrexcptn + stubs_offset
  30. W(b)    vector_irq + stubs_offset
  31. W(b)    vector_fiq + stubs_offset
  32. .globl  __vectors_end
  33. __vectors_end:

代码被分为两部分:

  • 第一部分是真正的向量跳转表,位于__vectors_start和__vectors_end之间;
  • 第二部分是处理跳转的部分,位于__stubs_start和__stubs_end之间;

[plain] view plain copy

  1. vector_stub irq, IRQ_MODE, 4

以上这一句把宏展开后实际上就是定义了vector_irq,根据进入中断前的cpu模式,分别跳转到__irq_usr或__irq_svc。

[plain] view plain copy

  1. vector_stub dabt, ABT_MODE, 8

以上这一句把宏展开后实际上就是定义了vector_dabt,根据进入中断前的cpu模式,分别跳转到__dabt_usr或__dabt_svc。

系统启动阶段,位于arch/arm/kernel/traps.c中的early_trap_init()被调用:

[cpp] view plain copy

  1. void __init early_trap_init(void)
  2. {
  3. ......
  4. /*
  5. * Copy the vectors, stubs and kuser helpers (in entry-armv.S)
  6. * into the vector page, mapped at 0xffff0000, and ensure these
  7. * are visible to the instruction stream.
  8. */
  9. memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
  10. memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
  11. ......
  12. }

以上两个memcpy会把__vectors_start开始的代码拷贝到0xffff0000处,把__stubs_start开始的代码拷贝到0xFFFF0000+0x200处,这样,异常中断到来时,CPU就可以正确地跳转到相应中断向量入口并执行他们。

                                                                        图1.1  Linux中ARM体系的中断向量拷贝过程

对于系统的外部设备来说,通常都是使用IRQ中断,所以我们只关注__irq_usr和__irq_svc,两者的区别是进入和退出中断时是否进行用户栈和内核栈之间的切换,还有进程调度和抢占的处理等,这些细节不在这里讨论。两个函数最终都会进入irq_handler这个宏:

[plain] view plain copy

  1. .macro  irq_handler
  2. #ifdef CONFIG_MULTI_IRQ_HANDLER
  3. ldr r1, =handle_arch_irq
  4. mov r0, sp
  5. adr lr, BSYM(9997f)
  6. ldr pc, [r1]
  7. #else
  8. arch_irq_handler_default
  9. #endif
  10. 9997:
  11. .endm

如果选择了MULTI_IRQ_HANDLER配置项,则意味着允许平台的代码可以动态设置irq处理程序,平台代码可以修改全局变量:handle_arch_irq,从而可以修改irq的处理程序。这里我们讨论默认的实现:arch_irq_handler_default,它位于arch/arm/include/asm/entry_macro_multi.S中:

[plain] view plain copy

  1. .macro  arch_irq_handler_default
  2. get_irqnr_preamble r6, lr
  3. 1:  get_irqnr_and_base r0, r2, r6, lr
  4. movne   r1, sp
  5. @
  6. @ routine called with r0 = irq number, r1 = struct pt_regs *
  7. @
  8. adrne   lr, BSYM(1b)
  9. bne asm_do_IRQ
  10. ......

get_irqnr_preamble和get_irqnr_and_base两个宏由machine级的代码定义,目的就是从中断控制器中获得IRQ编号,紧接着就调用asm_do_IRQ,从这个函数开始,中断程序进入C代码中,传入的参数是IRQ编号和寄存器结构指针,这个函数在arch/arm/kernel/irq.c中实现:

[cpp] view plain copy

  1. /*
  2. * asm_do_IRQ is the interface to be used from assembly code.
  3. */
  4. asmlinkage void __exception_irq_entry
  5. asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
  6. {
  7. handle_IRQ(irq, regs);
  8. }

到这里,中断程序完成了从asm代码到C代码的传递,并且获得了引起中断的IRQ编号。

2.  初始化

与通用中断子系统相关的初始化由start_kernel()函数发起,调用流程如下图所视:

图2.1  通用中断子系统的初始化

  • 首先,在setup_arch函数中,early_trap_init被调用,其中完成了第1节所说的中断向量的拷贝和重定位工作。
  • 然后,start_kernel发出early_irq_init调用,early_irq_init属于与硬件和平台无关的通用逻辑层,它完成irq_desc结构的内存申请,为它们其中某些字段填充默认值,完成后调用体系相关的arch_early_irq_init函数完成进一步的初始化工作,不过ARM体系没有实现arch_early_irq_init。
  • 接着,start_kernel发出init_IRQ调用,它会直接调用所属板子machine_desc结构体中的init_irq回调。machine_desc通常在板子的特定代码中,使用MACHINE_START和MACHINE_END宏进行定义。
  • machine_desc->init_irq()完成对中断控制器的初始化,为每个irq_desc结构安装合适的流控handler,为每个irq_desc结构安装irq_chip指针,使他指向正确的中断控制器所对应的irq_chip结构的实例,同时,如果该平台中的中断线有多路复用(多个中断公用一个irq中断线)的情况,还应该初始化irq_desc中相应的字段和标志,以便实现中断控制器的级联。

3.  中断控制器的软件抽象:struct irq_chip

正如上一篇文章Linux中断(interrupt)子系统之一:中断系统基本原理所述,所有的硬件中断在到达CPU之前,都要先经过中断控制器进行汇集,合乎要求的中断请求才会通知cpu进行处理,中断控制器主要完成以下这些功能:

  • 对各个irq的优先级进行控制;
  • 向CPU发出中断请求后,提供某种机制让CPU获得实际的中断源(irq编号);
  • 控制各个irq的电气触发条件,例如边缘触发或者是电平触发;
  • 使能(enable)或者屏蔽(mask)某一个irq;
  • 提供嵌套中断请求的能力;
  • 提供清除中断请求的机制(ack);
  • 有些控制器还需要CPU在处理完irq后对控制器发出eoi指令(end of interrupt);
  • 在smp系统中,控制各个irq与cpu之间的亲缘关系(affinity);

通用中断子系统把中断控制器抽象为一个数据结构:struct irq_chip,其中定义了一系列的操作函数,大部分多对应于上面所列的某个功能:

[cpp] view plain copy

  1. struct irq_chip {
  2. const char  *name;
  3. unsigned int    (*irq_startup)(struct irq_data *data);
  4. void        (*irq_shutdown)(struct irq_data *data);
  5. void        (*irq_enable)(struct irq_data *data);
  6. void        (*irq_disable)(struct irq_data *data);
  7. void        (*irq_ack)(struct irq_data *data);
  8. void        (*irq_mask)(struct irq_data *data);
  9. void        (*irq_mask_ack)(struct irq_data *data);
  10. void        (*irq_unmask)(struct irq_data *data);
  11. void        (*irq_eoi)(struct irq_data *data);
  12. int     (*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);
  13. int     (*irq_retrigger)(struct irq_data *data);
  14. int     (*irq_set_type)(struct irq_data *data, unsigned int flow_type);
  15. int     (*irq_set_wake)(struct irq_data *data, unsigned int on);
  16. void        (*irq_bus_lock)(struct irq_data *data);
  17. void        (*irq_bus_sync_unlock)(struct irq_data *data);
  18. void        (*irq_cpu_online)(struct irq_data *data);
  19. void        (*irq_cpu_offline)(struct irq_data *data);
  20. void        (*irq_suspend)(struct irq_data *data);
  21. void        (*irq_resume)(struct irq_data *data);
  22. void        (*irq_pm_shutdown)(struct irq_data *data);
  23. void        (*irq_print_chip)(struct irq_data *data, struct seq_file *p);
  24. unsigned long   flags;
  25. /* Currently used only by UML, might disappear one day.*/
  26. #ifdef CONFIG_IRQ_RELEASE_METHOD
  27. void        (*release)(unsigned int irq, void *dev_id);
  28. #endif
  29. };

各个字段解释如下:

name  中断控制器的名字,会出现在 /proc/interrupts中。

irq_startup  第一次开启一个irq时使用。

irq_shutdown  与irq_starup相对应。

irq_enable  使能该irq,通常是直接调用irq_unmask()。

irq_disable  禁止该irq,通常是直接调用irq_mask,严格意义上,他俩其实代表不同的意义,disable表示中断控制器根本就不响应该irq,而mask时,中断控制器可能响应该irq,只是不通知CPU,这时,该irq处于pending状态。类似的区别也适用于enable和unmask。

irq_ack  用于CPU对该irq的回应,通常表示cpu希望要清除该irq的pending状态,准备接受下一个irq请求。

irq_mask  屏蔽该irq。

irq_unmask  取消屏蔽该irq。

irq_mask_ack  相当于irq_mask + irq_ack。

irq_eoi  有些中断控制器需要在cpu处理完该irq后发出eoi信号,该回调就是用于这个目的。

irq_set_affinity  用于设置该irq和cpu之间的亲缘关系,就是通知中断控制器,该irq发生时,那些cpu有权响应该irq。当然,中断控制器会在软件的配合下,最终只会让一个cpu处理本次请求。

irq_set_type  设置irq的电气触发条件,例如IRQ_TYPE_LEVEL_HIGH或IRQ_TYPE_EDGE_RISING。

irq_set_wake  通知电源管理子系统,该irq是否可以用作系统的唤醒源。

以上大部分的函数接口的参数都是irq_data结构指针,irq_data结构的由来在上一篇文章已经说过,这里仅贴出它的定义,各字段的意义请参考注释:

[cpp] view plain copy

  1. /**
  2. * struct irq_data - per irq and irq chip data passed down to chip functions
  3. * @irq:        interrupt number
  4. * @hwirq:      hardware interrupt number, local to the interrupt domain
  5. * @node:       node index useful for balancing
  6. * @state_use_accessors: status information for irq chip functions.
  7. *          Use accessor functions to deal with it
  8. * @chip:       low level interrupt hardware access
  9. * @domain:     Interrupt translation domain; responsible for mapping
  10. *          between hwirq number and linux irq number.
  11. * @handler_data:   per-IRQ data for the irq_chip methods
  12. * @chip_data:      platform-specific per-chip private data for the chip
  13. *          methods, to allow shared chip implementations
  14. * @msi_desc:       MSI descriptor
  15. * @affinity:       IRQ affinity on SMP
  16. *
  17. * The fields here need to overlay the ones in irq_desc until we
  18. * cleaned up the direct references and switched everything over to
  19. * irq_data.
  20. */
  21. struct irq_data {
  22. unsigned int        irq;
  23. unsigned long       hwirq;
  24. unsigned int        node;
  25. unsigned int        state_use_accessors;
  26. struct irq_chip     *chip;
  27. struct irq_domain   *domain;
  28. void            *handler_data;
  29. void            *chip_data;
  30. struct msi_desc     *msi_desc;
  31. #ifdef CONFIG_SMP
  32. cpumask_var_t       affinity;
  33. #endif
  34. };

根据设备使用的中断控制器的类型,体系架构的底层的开发只要实现上述接口中的各个回调函数,然后把它们填充到irq_chip结构的实例中,最终把该irq_chip实例注册到irq_desc.irq_data.chip字段中,这样各个irq和中断控制器就进行了关联,只要知道irq编号,即可得到对应到irq_desc结构,进而可以通过chip指针访问中断控制器。

4.  进入流控处理层

进入C代码的第一个函数是asm_do_IRQ,在ARM体系中,这个函数只是简单地调用handle_IRQ:

[cpp] view plain copy

  1. /*
  2. * asm_do_IRQ is the interface to be used from assembly code.
  3. */
  4. asmlinkage void __exception_irq_entry
  5. asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
  6. {
  7. handle_IRQ(irq, regs);
  8. }

handle_IRQ本身也不是很复杂:

[cpp] view plain copy

  1. void handle_IRQ(unsigned int irq, struct pt_regs *regs)
  2. {
  3. struct pt_regs *old_regs = set_irq_regs(regs);
  4. irq_enter();
  5. /*
  6. * Some hardware gives randomly wrong interrupts.  Rather
  7. * than crashing, do something sensible.
  8. */
  9. if (unlikely(irq >= nr_irqs)) {
  10. if (printk_ratelimit())
  11. printk(KERN_WARNING "Bad IRQ%u\n", irq);
  12. ack_bad_irq(irq);
  13. } else {
  14. generic_handle_irq(irq);
  15. }
  16. /* AT91 specific workaround */
  17. irq_finish(irq);
  18. irq_exit();
  19. set_irq_regs(old_regs);
  20. }

irq_enter主要是更新一些系统的统计信息,同时在__irq_enter宏中禁止了进程的抢占:

[cpp] view plain copy

  1. #define __irq_enter()                   \
  2. do {                        \
  3. account_system_vtime(current);      \
  4. add_preempt_count(HARDIRQ_OFFSET);  \
  5. trace_hardirq_enter();          \
  6. } while (0)

CPU一旦响应IRQ中断后,ARM会自动把CPSR中的I位置位,表明禁止新的IRQ请求,直到中断控制转到相应的流控层后才通过local_irq_enable()打开。你可能会奇怪,既然此时的irq中断都是都是被禁止的,为何还要禁止抢占?这是因为要考虑中断嵌套的问题,一旦流控层或驱动程序主动通过local_irq_enable打开了IRQ,而此时该中断还没处理完成,新的irq请求到达,这时代码会再次进入irq_enter,在本次嵌套中断返回时,内核不希望进行抢占调度,而是要等到最外层的中断处理完成后才做出调度动作,所以才有了禁止抢占这一处理。

下一步,generic_handle_irq被调用,generic_handle_irq是通用逻辑层提供的API,通过该API,中断的控制被传递到了与体系结构无关的中断流控层:

[cpp] view plain copy

  1. int generic_handle_irq(unsigned int irq)
  2. {
  3. struct irq_desc *desc = irq_to_desc(irq);
  4. if (!desc)
  5. return -EINVAL;
  6. generic_handle_irq_desc(irq, desc);
  7. return 0;
  8. }

最终会进入该irq注册的流控处理回调中:

[cpp] view plain copy

  1. static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
  2. {
  3. desc->handle_irq(irq, desc);
  4. }

5.  中断控制器的级联

在实际的设备中,经常存在多个中断控制器,有时多个中断控制器还会进行所谓的级联。为了方便讨论,我们把直接和CPU相连的中断控制器叫做根控制器,另外一些和跟控制器相连的叫子控制器。根据子控制器的位置,我们把它们分为两种类型:

  • 机器级别的级联  子控制器位于SOC内部,或者子控制器在SOC的外部,但是是某个板子系列的标准配置,如图5.1的左边所示;
  • 设备级别的级联  子控制器位于某个外部设备中,用于汇集该设备发出的多个中断,如图5.1的右边所示;

图5.1  中断控制器的级联类型

对于机器级别的级联,级联的初始化代码理所当然地位于板子的初始化代码中(arch/xxx/mach-xxx),因为只要是使用这个板子或SOC的设备,必然要使用这个子控制器。而对于设备级别的级联,因为该设备并不一定是系统的标配设备,所以中断控制器的级联操作应该在该设备的驱动程序中实现。机器设备的级联,因为得益于事先已经知道子控制器的硬件连接信息,内核可以方便地为子控制器保留相应的irq_desc结构和irq编号,处理起来相对简单。设备级别的级联则不一样,驱动程序必须动态地决定组合设备中各个子设备的irq编号和irq_desc结构。本章我只讨论机器级别的级联,设备级别的关联可以使用同样的原理,也可以实现为共享中断,我会在本系列接下来的文章中讨论。

要实现中断控制器的级联,要使用以下几个的关键数据结构字段和通用中断逻辑层的API:

irq_desc.handle_irq  irq的流控处理回调函数,子控制器在把多个irq汇集起来后,输出端连接到根控制器的其中一个irq中断线输入脚,这意味着,每个子控制器的中断发生时,CPU一开始只会得到根控制器的irq编号,然后进入该irq编号对应的irq_desc.handle_irq回调,该回调我们不能使用流控层定义好的几个流控函数,而是要自己实现一个函数,该函数负责从子控制器中获得irq的中断源,并计算出对应新的irq编号,然后调用新irq所对应的irq_desc.handle_irq回调,这个回调使用流控层的标准实现。

irq_set_chained_handler()  该API用于设置根控制器与子控制器相连的irq所对应的irq_desc.handle_irq回调函数,并且设置IRQ_NOPROBE和IRQ_NOTHREAD以及IRQ_NOREQUEST标志,这几个标志保证驱动程序不会错误地申请该irq,因为该irq已经被作为级联irq使用。

irq_set_chip_and_handler()  该API同时设置irq_desc中的handle_irq回调和irq_chip指针。

以下例子代码位于:/arch/arm/plat-s5p/irq-eint.c:

[cpp] view plain copy

  1. int __init s5p_init_irq_eint(void)
  2. {
  3. int irq;
  4. for (irq = IRQ_EINT(0); irq <= IRQ_EINT(15); irq++)
  5. irq_set_chip(irq, &s5p_irq_vic_eint);
  6. for (irq = IRQ_EINT(16); irq <= IRQ_EINT(31); irq++) {
  7. irq_set_chip_and_handler(irq, &s5p_irq_eint, handle_level_irq);
  8. set_irq_flags(irq, IRQF_VALID);
  9. }
  10. irq_set_chained_handler(IRQ_EINT16_31, s5p_irq_demux_eint16_31);
  11. return 0;
  12. }

该SOC芯片的外部中断:IRQ_EINT(0)到IRQ_EINT(15),每个引脚对应一个根控制器的irq中断线,它们是正常的irq,无需级联。IRQ_EINT(16)到IRQ_EINT(31)经过子控制器汇集后,统一连接到根控制器编号为IRQ_EINT16_31这个中断线上。可以看到,子控制器对应的irq_chip是s5p_irq_eint,子控制器的irq默认设置为电平中断的流控处理函数handle_level_irq,它们通过API:irq_set_chained_handler进行设置。如果根控制器有128个中断线,IRQ_EINT0--IRQ_EINT15通常占据128内的某段连续范围,这取决于实际的物理连接。IRQ_EINT16_31因为也属于跟控制器,所以它的值也会位于128以内,但是IRQ_EINT16--IRQ_EINT31通常会在128以外的某段范围,这时,代表irq数量的常量NR_IRQS,必须考虑这种情况,定义出超过128的某个足够的数值。级联的实现主要依靠编号为IRQ_EINT16_31的流控处理程序:s5p_irq_demux_eint16_31,它的最终实现类似于以下代码:

[cpp] view plain copy

  1. static inline void s5p_irq_demux_eint(unsigned int start)
  2. {
  3. u32 status = __raw_readl(S5P_EINT_PEND(EINT_REG_NR(start)));
  4. u32 mask = __raw_readl(S5P_EINT_MASK(EINT_REG_NR(start)));
  5. unsigned int irq;
  6. status &= ~mask;
  7. status &= 0xff;
  8. while (status) {
  9. irq = fls(status) - 1;
  10. generic_handle_irq(irq + start);
  11. status &= ~(1 << irq);
  12. }
  13. }

在获得新的irq编号后,它的最关键的一句是调用了通用中断逻辑层的API:generic_handle_irq,这时它才真正地把中断控制权传递到中断流控层中来。

时间: 2024-09-30 06:54:53

Linux中断(interrupt)子系统之二:arch相关的硬件封装层【转】的相关文章

Linux中断(interrupt)子系统之一:中断系统基本原理

这个中断系列文章主要针对移动设备中的Linux进行讨论,文中的例子基本都是基于ARM这一体系架构,其他架构的原理其实也差不多,区别只是其中的硬件抽象层.内核版本基于3.3.虽然内核的版本不断地提升,不过自从上一次变更到当前的通用中断子系统后,大的框架性的东西并没有太大的改变. /*****************************************************************************************************/ 声明:本博内容

Linux中断(interrupt)子系统

Linux中断(interrupt)子系统之一:中断系统基本原理 Linux中断(interrupt)子系统之二:arch相关的硬件封装层 Linux中断(interrupt)子系统之三:中断流控处理层 Linux中断(interrupt)子系统之四:驱动程序接口层 & 中断通用逻辑层 Linux中断(interrupt)子系统之五:软件中断(softIRQ) http://blog.csdn.net/droidphone/article/details/7497787

Linux中断(interrupt)子系统之一:中断系统基本原理【转】

转自:http://blog.csdn.net/droidphone/article/details/7445825 这个中断系列文章主要针对移动设备中的Linux进行讨论,文中的例子基本都是基于ARM这一体系架构,其他架构的原理其实也差不多,区别只是其中的硬件抽象层.内核版本基于3.3.虽然内核的版本不断地提升,不过自从上一次变更到当前的通用中断子系统后,大的框架性的东西并没有太大的改变. /***************************************************

linux中断子系统:中断号的映射与维护

写在前沿: 好久好久没有静下心来整理一些东西了,开始工作已有一个月,脑子里想整理的东西特别多.记录是一种很好的自我学习方式,静下来多思考多总结,三年的工作目标不能发生变化,作为职场菜鸟即将进入全世界半导体第一的Intel working,是机遇更是一种挑战,困难也是可想而知.脚踏实地.仰望星空,以结果为导向,以目标为准则,争取每天进步一点点. Linux内核版本:3.4.39 一. linux中断子系统的irq_desc初始化 linux内核最初的中断初始化过程入口为start_kernel.在

linux中断源码分析 - 初始化(二)

本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 本篇文章主要讲述源码中是如何对中断进行一系列的初始化的. 回顾 在上一篇概述中,介绍了几个对于中断来说非常重要的数据结构,分别是:中断描述符表,中断描述符数组,中断描述符,中断控制器描述符,中断服务例程.可以说这几个结构组成了整个内核中断框架主体,所以内核对整个中断的初始化工作大多集中在了这几个结构上. 在系统中,当一个中断产生时,首先CPU会从中断描述符表中获取相应的中断向量,并根据中断向量的权限位判断是否

Linux usb子系统(二):USB设备驱动usb-skeleton.c

usb驱动分为通过usbfs操作设备的用户空间驱动,内核空间的内核驱动.两者不能同时进行,否则容易引发对共享资源访问的问题,死锁!使用了内核驱动,就不能在usbfs里驱动该设备. 下面转载的一篇分析usb-skeleton.c文章. 初次接触与OS相关的设备驱动编写,感觉还挺有意思的,为了不至于忘掉看过的东西,笔记跟总结当然不可缺,更何况我决定为嵌入式卖命了.好,言归正传,我说一说这段时间的收获,跟大家分享一下Linux的驱动开发.但这次只先针对Linux的USB子系统作分析,因为周五研讨老板催

Linux环境编程之信号(二):不可靠信号、中断的系统调用、可重入函数

(一)不可靠信号 对前面说的信号,是不可靠的,不可靠指的是信号可能会丢失:一个信号发生了,但进程却可能一直不知道这一点.另外,进程对信号的控制能力有限,只能捕捉信号或忽略它.有时用户希望通知内核阻塞一个信号:不要忽略它,在其发生时记住它,然后在进程做好准备时再通知它.这种阻塞信号的能力并不具备. 之前的版本中村咋一个问题:在进程每次接到信号对其进行处理时,随即将该信号动作复位为默认值.另一个问题是,在进程不希望某种信号发生时,它不能关闭该信号.进程能做的一切就是忽略该信号. (二)中断的系统调用

Linux中断 - GIC代码分析

一.前言 GIC(Generic Interrupt Controller)是ARM公司提供的一个通用的中断控制器,其architecture specification目前有四个版本,V1-V4(V2最多支持8个ARM core,V3/V4支持更多的ARM core,主要用于ARM64服务器系统结构).目前在ARM官方网站只能下载到Version 2的GIC architecture specification,因此,本文主要描述符合V2规范的GIC硬件及其驱动. 具体GIC硬件的实现形态有两

Linux中断 - IRQ Domain介绍

一.概述 在linux kernel中,我们使用下面两个ID来标识一个来自外设的中断: 1.IRQ number.CPU需要为每一个外设中断编号,我们称之IRQ Number.这个IRQ number是一个虚拟的interrupt ID,和硬件无关,仅仅是被CPU用来标识一个外设中断. 2.HW interrupt ID.对于interrupt controller而言,它收集了多个外设的interrupt request line并向上传递,因此,interrupt controller需要对