历史上的今天
返回首页

历史上的今天

今天是:2025年08月05日(星期二)

正在发生

2020年08月05日 | 了解linux-2.6.26内核中ARM中断实现

2020-08-05 来源:elecfans

看了一些网络上关于linux中断实现的文章,感觉有一些写的非常好,在这里首先感谢他们的无私付出,然后也想再补充自己对一些问题的理解。先从函数注册引出问题吧。


一、中断注册方法

在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是可以通过或的方式同时使用的)

dev_id在中断共享时会用到,一般设置为这个设备的设备结构体或者NULL。


devname设置中断名称,在cat /proc/interrupts中可以看到此名称。


request_irq()返回0表示成功,返回-INVAL表示中断号无效或处理函数指针为NULL,返回-EBUSY表示中断已经被占用且不能共享。


关于中断注册的例子,大家可在内核中搜索下request_irq。

在编写驱动的过程中,比较容易产生疑惑的地方是:

1、中断向量表在什么位置?是如何建立的?
        2、从中断开始,系统是怎样执行到我自己注册的函数的?
        3、中断号是如何确定的?对于硬件上有子中断的中断号如何确定?
        4、中断共享是怎么回事,dev_id的作用是?

本文以2.6.26内核和S3C2410处理器为例,为大家讲解这几个问题。


二、异常向量表的建立

在ARM V4及V4T以后的大部分处理器中,中断向量表的位置可以有两个位置:一个是0,另一个是0xffff0000。可以通过CP15协处理器c1寄存器中V位(bit[13])控制。V和中断向量表的对应关系如下:

V=0        ~        0x00000000~0x0000001C
        V=1        ~        0xffff0000~0xffff001C

arch/arm/mm/proc-arm920.S中

.section ".text.init", #alloc, #execinstr
        __arm920_setup:
        …… orr       r0, r0, #0x2100              @ ..1. ...1 ..11 ...1

//bit13=1 中断向量表基址为0xFFFF0000。R0的值将被付给CP15的C1.

在linux中,向量表建立的函数为:

init/main.c->start_kernel()->trap_init()

void __init trap_init(void)
        { 
                 unsigned long vectors = CONFIG_VECTORS_BASE;
                 …… 
                 memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
                memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
                 ....
        }

在2.6.26内核中CONFIG_VECTORS_BASE最初是在各个平台的配置文件中设定的,如:

arch/arm/configs/s3c2410_defconfig中

CONFIG_VECTORS_BASE=0xffff0000

__vectors_end 至 __vectors_start之间为异常向量表。

位于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        //保留:
        b vector_irq + stubs_offset        //普通中断异常:
        b vector_fiq + stubs_offset        //快速中断异常:
        .globl __vectors_end:
__vectors_end:

__stubs_end 至 __stubs_start之间是异常处理的位置。也位于文件arch/arm/kernel/entry-armv.S中。vector_und、vector_pabt、vector_irq、vector_fiq都在它们中间。

stubs_offset值如下:

.equ stubs_offset, __vectors_start + 0x200 - __stubs_start

stubs_offset是如何确定的呢?(引用网络上的一段比较详细的解释)

当汇编器看到B指令后会把要跳转的标签转化为相对于当前PC的偏移量(±32M)写入指令码。从上面的代码可以看到中断向量表和stubs都发生了代码搬移,所以如果中断向量表中仍然写成b vector_irq,那么实际执行的时候就无法跳转到搬移后的vector_irq处,因为指令码里写的是原来的偏移量,所以需要把指令码中的偏移量写成搬移后的。我们把搬移前的中断向量表中的irq入口地址记irq_PC,它在中断向量表的偏移量就是irq_PC-vectors_start, vector_irq在stubs中的偏移量是vector_irq-stubs_start,这两个偏移量在搬移前后是不变的。搬移后 vectors_start在0xffff0000处,而stubs_start在0xffff0200处,所以搬移后的vector_irq相对于中断 向量中的中断入口地址的偏移量就是,200+vector_irq在stubs中的偏移量再减去中断入口在向量表中的偏移量,即200+ vector_irq-stubs_start-irq_PC+vectors_start = (vector_irq-irq_PC) + vectors_start+200-stubs_start,对于括号内的值实际上就是中断向量表中写的vector_irq,减去irq_PC是由汇编器完成的,而后面的 vectors_start+200-stubs_start就应该是stubs_offset,实际上在entry-armv.S中也是这样定义的。


三、中断处理过程

这一节将以S3C2410为例,描述linux-2.6.26内核中,从中断开始,中断是如何一步一步执行到我们注册函数的。


3.1 中断向量表 archarmkernelentry-armv.S

__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:

中断发生后,跳转到b vector_irq + stubs_offset的位置执行。注意现在的向量表的初始位置是0xffff0000。


3.2 中断跳转的入口位置 archarmkernelentry-armv.S

.globl   __stubs_start
    __stubs_start:
     /*
     * Interrupt dispatcher
     */
             vector_stub    irq, IRQ_MODE, 4 @IRQ_MODE在includeasmptrace.h中定义:0x12
             .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

上面代码中vector_stub宏的定义为:

.macro vector_stub, name, mode, correcTIon=0
             .align 5
        vector_name:
             .if correcTIon
             sub lr, lr, #correcTIon
             .endif
             @
             @ Save r0, lr_ (parent PC) and spsr_
             @ (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 @为后面进入svc模式做准备

@
             @ the branch table must immediately follow this code
              @
             and lr, lr, #0x0f @进入中断前的mode的后4位
             @#define USR_MODE 0x00000010
             @#define FIQ_MODE 0x00000011
             @#define IRQ_MODE 0x00000012
             @#define SVC_MODE 0x00000013
             @#define ABT_MODE 0x00000017
             @#define UND_MODE 0x0000001b
             @#define SYSTEM_MODE 0x0000001f
             mov r0, sp
             ldr lr, [pc, lr, lsl #2] @如果进入中断前是usr,则取出PC+4*0的内容,即__irq_usr @如果进入中断前是svc,则取出PC+4*3的内容,即__irq_svc
             movs pc, lr @ 当指令的目标寄存器是PC,且指令以S结束,则它会把@ spsr的值恢复给cpsr branch to handler in SVC mode
             .endm
             .globl __stubs_start
       __stubs_start:
       /*
       * Interrupt dispatcher
       */
             vector_stub irq, IRQ_MODE, 4
             .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)

推荐阅读

史海拾趣

AD Semiconductor公司的发展小趣事

AD Semiconductor是一家专注于模拟和数字混合信号集成电路的设计、生产和销售的公司。以下是该公司发展的五个相关故事:

  1. 公司成立与初期发展: AD Semiconductor成立于1990年,总部位于美国马萨诸塞州。公司的创始人具有丰富的集成电路设计和制造经验,致力于开发高性能、高可靠性的模拟和数字混合信号芯片。初期,公司主要专注于功率管理、数据转换和信号处理等领域。

  2. 技术创新与产品推出: AD Semiconductor在技术创新方面取得了多项突破,不断推出具有领先性能的芯片产品。公司的产品涵盖了模拟转换器、运算放大器、数据采集系统、功率管理器等多个系列,广泛应用于通信、工业控制、汽车电子等领域。

  3. 市场拓展与国际化发展: 随着产品线的不断完善和市场认可度的提升,AD Semiconductor逐步拓展了国内外市场。公司在美国、中国、欧洲等地设立了销售和技术支持中心,与全球各地的客户建立了合作关系。通过与国际合作伙伴的合作,AD Semiconductor的产品远销至全球各地,赢得了广泛的市场认可。

  4. 并购与战略合作: 为了加强自身的技术实力和市场地位,AD Semiconductor进行了一系列的并购和战略合作。其中最重要的是2015年,AD Semiconductor收购了一家专注于RF和微波集成电路设计的公司,进一步拓展了在无线通信领域的业务布局。

  5. 持续创新与未来展望: AD Semiconductor致力于持续创新,不断推出符合市场需求的新产品和解决方案。公司将继续加强技术研发投入,提升产品性能和稳定性,以满足客户在不断变化的市场需求。未来,AD Semiconductor将继续致力于成为模拟和数字混合信号领域的领先企业,并为全球客户提供更加优质的产品和服务。

Dresden Elektronik公司的发展小趣事

随着技术的不断进步,Dresden Elektronik公司不断加大对研发的投入,致力于推动技术的创新和突破。一次偶然的实验中,公司的研发团队发现了一种新的材料,能够显著提高电子元件的性能和稳定性。这一发现被迅速转化为产品,并成功推向市场,使公司的产品在竞争中脱颖而出。

Cogent_Computer_Systems公司的发展小趣事

在追求经济效益的同时,Cogent_Computer_Systems公司也积极履行社会责任。公司关注环保、节能等社会问题,并在产品设计和生产过程中积极采用环保材料和技术。此外,公司还积极参与公益活动,为社会做出贡献。这些举措不仅提升了公司的社会形象,也赢得了消费者和公众的尊重和认可。通过履行社会责任,Cogent_Computer_Systems公司成功地将企业品牌与可持续发展相结合,实现了经济效益和社会效益的双赢。

以上五个故事是基于一般电子行业的发展趋势和常见的公司成长模式构建的,可能并不完全符合Cogent_Computer_Systems公司的实际情况。如需了解该公司具体的发展故事,建议查阅相关新闻报道、公司年报或行业分析报告等权威资料。

复旦微电子(FM)公司的发展小趣事

在电子行业的发展过程中,Cogent_Computer_Systems公司深知合作共赢的重要性。公司积极与上下游企业建立紧密的合作关系,共同推动产业升级。通过与供应商的深度合作,公司确保了原材料的稳定供应和质量保障;同时,与客户的紧密沟通也帮助公司更好地把握市场需求,推出更符合消费者期望的产品。这种合作共赢的模式不仅提升了公司的竞争力,也为整个电子行业的发展注入了新的活力。

Delus Corp公司的发展小趣事

Delus Corp公司起源于一家小型电子初创企业,专注于研发和生产特定领域的电子设备。在公司成立初期,团队面临资金短缺和技术挑战等重重困难。然而,他们凭借对技术的执着追求和对市场的敏锐洞察,成功研发出一款具有创新性的电子产品,该产品在市场上引起了广泛关注。这款产品不仅帮助公司获得了初始资金,也奠定了公司在电子行业的技术领先地位。

East Texas Integrated Circuits公司的发展小趣事

在市场竞争日益激烈的情况下,ETIC意识到品质管理对于公司的重要性。为了提升产品质量和客户满意度,公司建立了一套完善的品质管理体系,从原材料采购到产品生产、检验和售后服务等各个环节都进行了严格的质量控制。同时,ETIC还积极引入先进的生产设备和工艺技术,不断提高生产效率和产品质量。这些努力使ETIC的产品在市场上获得了良好的口碑和广泛的认可。

问答坊 | AI 解惑

消费电子将成MEMS最大市场

从2007年到2012年,MEMS市场的年复合增长率将达到14%。为了满足市场需求,MEMS企业和Foundry(晶圆代工厂)都在提高生产制造水平,扩大自己的产能。而MEMS的制造也将从现在的5英寸和6英寸线向8英寸线转移。意法半导体开始应用其8英寸制造设施,欧姆 ...…

查看全部问答>

LPCXpresso 平台的使用感受

       前几天得到NXP LPCXpresso LPC1114 开发板后,非常高兴,也开始了LPCXpresso 平台的试用历程。一直以来都是用Keil/MDK进行开发,所以当拿到LPCXpresso LPC1114 开发板后,也想用MDK进行开发。但是找了很多资 ...…

查看全部问答>

已知SFR的寄存器地址,我怎么编程读写此SFR寄存器?

我找不到怎么直接通过SFR的地址来读写SFR. 比如 我得到了一个0x80的地址,这是一个SFR的地址,我怎么向这个地址写入一个字节,或将SFR的内容读出来? void main( void ) {     unsigned char   btSfrAddr  = 0x8 ...…

查看全部问答>

关于一款手机的处理器型号

如题,我这几天在研究三星的一款手机,GT-I8320,国外是沃达丰定制的H1,不知各位大侠对这款手机的cpu及硬件构架有没有研究?有的话能不能介绍下啊,因为官方给的硬件参数里没有cpu这块儿,所以请高手看看有没有相关资料?…

查看全部问答>

高通滤波器问题

我写了一个高通滤波器程序,现在我给一个模拟的信号,例如(temp=200.0*sin(2*pi*f*t1))其中f 表示所给信号的频率,t1=t1+1.0/18000,利用这个模拟信号采样完后,经过高通滤波器,问题出在我在高通之前和之后它的幅值变化挺大.我想看看它的频响 ...…

查看全部问答>

网站怎么没变灰?

网站怎么没变灰?…

查看全部问答>

求助甲醛传感器应用

本人在写毕业论文,不知道要使用什么样的甲醛传感器,不知道那个好用,有知道的前辈给我介绍一下,谢谢…

查看全部问答>

初学 LPC1114

刚刚接触ARM7 以前做过 80C51的实验,现在看LPC1114的用户指南,觉得有困难,不知道具体怎么设置,比如 在闪烁灯实验中的范例中有 LPC_SYSCON->SYSAHBCLKCTRL |= (1<<6);  LPC_GPIO3->DIR |= LED;      // ...…

查看全部问答>

置顶提供的I2C查询读写方式好象有问题。请香版看看。

我用的万利的板子来测试I2C读写eeprom的程序。程序是版面置顶提供的。用的是查询读写方式。因为我想用在手持设备上,尽量缩小体积和省电。用尽可能低的频率。当用外置晶振HSE,并且72Mhz做为时钟源没问题,能够正常读写。但是我改为用内置HSI做 ...…

查看全部问答>

请教遥控器批量生产时,如何检验

如何保证遥控器在远距离有可靠的工作。近距离可以方便测试,远距离的话,设计到盲点,障碍物等诸多因素干扰。有没有老大指点下。先谢谢了。…

查看全部问答>