request_irq 中断号

本文详细解析了S3C2410处理器的中断处理过程,包括中断号的获取方式、中断描述符表的初始化以及特定中断号的修正逻辑。通过实例展示了如何正确配置和使用中断。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上面在说到安装中断的时候说过,调用request_irq()时的参数中irq的确定是个难题,为什么?

你如果到网络上查一下关于linux的资料,十有八九是关于i386体系结构上的,但linux是可以运行在多种cpu上的,比如采用arm内核的s3c2410,在i386体系上的经验在这里可以用么?我们试验一下:硬件准备,使用s3c2410的EINT0引脚作中断测试,为它编写一个中断驱动程序,最后将面临中断的安装,调用intrequest_irq(unsignedintirq,void (*handler)(int,void *,structpt_regs *),unsignedlongirq_flags,constchar *devname,void *dev_id),其余都好说,irq取什么值?很容易查到类似信息:在i386体系结构中,LINUX内核对中断向量号的使用规定如下:

       0-31号中断向量对应于异常类型,32-47号中断向量分配给可屏蔽硬件中断,48-255号分配给软件中断。所以中断控制器上的IRQ0对应于中断向量32。

       在ARM的手册上,所有的中断都叫异常,在S3C2410的手册上,其中断控制器的外部中断都叫EINTn,看起来EINT0就像是IRQ0 。

       那我们不如试试吧,按照经验,取irq=32,request_irq返回一个值,乃是-EINVAL,意思是说irq的取值非法,要么试试其他值,反正都不能正常工作。

怎么办?让我们寻根求源吧!

       文章开头说过:“当中断系统硬件产生一个中断信号,LINUX的中断处理系统将根据从硬件获得的中断号调用用户编写的中断处理程序”,我们看一下linux是如何取得中断号的过程,从中寻觅硬件的中断号如何对应到中断安装的系统调用中的参数的数值。中断产生后cpu进入中断向量指向的总的中断入口程序,对于arm的cpu来说,这个程序在arch/arm/kernel/entry-armv.S中的这部分:

vector_IRQ: @

  @savemodespecificregisters

  @

 ldrr13, .LCsirq

 sublr,lr, #4

 strlr, [r13]   @savelr_IRQ

 mrslr,spsr

 strlr, [r13, #4]   @savespsr_IRQ

  @

  @nowbranchtothereleventMODEhandlingroutine

  @ 

 movr13, #I_BIT |MODE_SVC

 msrspsr_c,r13   @switchtoSVC_32mode

 

 andlr,lr, #15

 ldrlr, [pc,lr,lsl #2]

 movspc,lr    @Changesmodeandbranches

 

.LCtab_irq: .word __irq_usr   @ 0  (USR_26 /USR_32)

  .word __irq_invalid   @ 1  (FIQ_26 /FIQ_32)

  .word __irq_invalid   @ 2  (IRQ_26 /IRQ_32)

  .word __irq_svc   @ 3  (SVC_26 /SVC_32)

  .word __irq_invalid   @ 4

  .word __irq_invalid   @ 5

  .word __irq_invalid   @ 6

  .word __irq_invalid   @ 7

  .word __irq_invalid   @ 8

  .word __irq_invalid   @ 9

  .word __irq_invalid   @ a

  .word __irq_invalid   @ b

  .word __irq_invalid   @ c

  .word __irq_invalid   @ d

  .word __irq_invalid   @ e

  .word __irq_invalid   @ f

 

  .align5

       根据注释并仔细研究这部分可知,中断产生后,程序保存一些于中断相关的特殊寄存器,改变cpu模式为svc模式并转到__irq_usr,这部分代码如下:

__irq_usr:subsp,sp, #S_FRAME_SIZE

 stmiasp, {r0 -r12}   @saver0 -r12

 ldrr4, .LCirq

 addr8,sp, #S_PC

 ldmiar4, {r5 -r7}   @getsavedPC,SPSR

 stmiar8, {r5 -r7}   @savepc,psr,old_r0

 stmdbr8, {sp,lr}^

 alignment_trapr4,r7, __temp_irq

 zero_fp

1: get_irqnr_and_baser0,r6,r5,lr

 movner1,sp

 adrsvcne,lr,1b

  @

  @routinecalledwithr0 =irqnumber,r1 =structpt_regs *

  @

 bnedo_IRQ

 movwhy, #0

 get_current_tasktsk

 bret_to_user

       注意get_irqnr_and_baser0,r6,r5,lr一句,这是一个宏,顾名思义,它将取得中断号,将这个宏展开,对于s3c2410来说,是其中的这部分代码:

#elifdefined(CONFIG_ARCH_S3C2410)

#include <asm/hardware.h>

 

  .macro disable_fiq

  .endm

 

  .macro get_irqnr_and_base,irqnr,irqstat,base,tmp

 movr4, #INTBASE  @virtualaddressofIRQregisters

 

 ldr /irqnr, [r4, #0x8] @readINTMSK

 ldr /irqstat, [r4, #0x10]   @readINTPND

 

 bics    /irqstat, /irqstat, /irqnr

 bics    /irqstat, /irqstat, /irqnr

 beq1002f

 

 mov /irqnr, #0

1001: tst /irqstat, #1

 bne1002f   @foundIRQ

 add /irqnr, /irqnr, #1

 mov /irqstat, /irqstat,lsr #1

 cmp /irqnr, #32

 bcc1001b

1002:

  .endm

 

  .macro irq_prio_table

  .endm  

       分析这段代码可知,这个宏取得中断悬挂寄存器的值,将它转换为中断位对应的数字值即EINTn对应n并返回到第一个参数中,比如EINT0上发生中断,在宏的第一个参数中返回0,EINT1上发生中断,在宏的第一个参数中返回1。

       再返回头来看__irq_usr的代码,根据注释和分析可知,代码在r0中存储irq号,r1指向一个包含发生中断时各种寄存器值的结构pt_regs,然后专向执行do_IRQ,函数do_IRQ()的代码在arch/arm/kernel/irq.c中:

asmlinkagevoiddo_IRQ(intirq,structpt_regs *regs)

{

structirqdesc *desc;

structirqaction *action;

intcpu;

 

irq =fixup_irq(irq);

 

 ……

 ……

}

       注意这个函数中的irq =fixup_irq(irq);一句,他对刚才取得的irq值进行了修正,对于s3c2410,这个动作在arch/arm/mach-s3c2410/irq.c中:

unsignedintfixup_irq(intirq) {

   unsignedintret;

   unsignedlongsub_mask,ext_mask;

 

   if (irq ==OS_TIMER)

     returnirq;

 

   switch (irq) {

   caseIRQ_UART0:

     sub_mask =SUBSRCPND & ~INTSUBMSK;

     ret =get_subIRQ(sub_mask,0,2,irq);

     break;

   caseIRQ_UART1:

     sub_mask =SUBSRCPND & ~INTSUBMSK;

     ret =get_subIRQ(sub_mask,3,5,irq);

     break;

   caseIRQ_UART2:

     sub_mask =SUBSRCPND & ~INTSUBMSK;

     ret =get_subIRQ(sub_mask,6,8,irq); 

     break;

   caseIRQ_ADCTC:

     sub_mask =SUBSRCPND & ~INTSUBMSK;

     ret =get_subIRQ(sub_mask,9,10,irq);

     break;

   caseIRQ_EINT4_7:

     ext_mask =EINTPEND & ~EINTMASK;

 

 

     ret =get_extIRQ(ext_mask,4,7,irq);

     break;

   caseIRQ_EINT8_23:

     ext_mask =EINTPEND & ~EINTMASK;

     ret =get_extIRQ(ext_mask,8,23,irq);

     break;

   default:

     ret =irq;

    }

 

   returnret;

}

       可见,对于EINT0,并不进行修正,而只对几个特殊中断号和多个中断脚复用一个中断号的情况进行修正。由此,我们可以得出S3C2410中,外部中断脚和中断号的对应关系是:

中断脚位                  中断号

INT_ADC       [31]

INT_RTC       [30] 

INT_SPI1       [29]

INT_UART0     [28]

INT_IIC         [27] 

INT_USBH      [26] 

INT_USBD      [25] 

Reserved        [24]

INT_UART1      [23] 

INT_SPI0        [22] 

INT_SDI         [21] 

INT_DMA3       [20]

INT_DMA2       [19]

 

INT_DMA1       [18] 

INT_DMA0       [17]

INT_LCD        [16] 

INT_UART2      [15]

INT_TIMER4     [14] 

INT_TIMER3     [13]

INT_TIMER2     [12] 

INT_TIMER1     [11] 

INT_TIMER0     [10]

INT_WDT        [9] 

INT_TICK        [8]

nBATT_FLT      [7] 

Reserved        [6] 

EINT8_23        [5] 

EINT4_7         [4] 

EINT3           [3]

EINT2           [2]

EINT1  

         [1]

EINT0           [0]

       那么,在前面的例子中,我们使用irq=0申请中断为什么也不行呢?我们来看一下系统是如何初始化中断数据的,对于s3c2410,系统在初始化中要调用下面的函数:

 

void __inits3c2410_init_irq(void) {

   intirq;

 

   request_resource(&iomem_resource, &irq_resource);

   request_resource(&iomem_resource, &eint_resource);

 

    /*disableallIRQs */

   INTMSK =0xffffffff;

   INTSUBMSK =0x7ff;

   EINTMASK =0x00fffff0;

 

    /*allIRQsareIRQ,notFIQ

      0 :IRQmode

      1 :FIQmode

    */

   INTMOD =0x00000000;

 

    /*clearSource/InterruptPendingRegister */

   SRCPND =0xffffffff;

   INTPND =0xffffffff;

   SUBSRCPND =0x7ff;

   EINTPEND =0x00fffff0;

 

    /*Defineirqhandler */

   for (irq=0;irq <NORMAL_IRQ_OFFSET;irq++) {

     irq_desc[irq].valid =1;

     irq_desc[irq].probe_ok =1;

     irq_desc[irq].mask_ack =s3c2410_mask_ack_irq;

     irq_desc[irq].mask =s3c2410_mask_irq;

     irq_desc[irq].unmask =s3c2410_unmask_irq;

    }

 

     irq_desc[IRQ_RESERVED6].valid =0;

     irq_desc[IRQ_RESERVED24].valid =0;

 

     irq_desc[IRQ_EINT4_7].valid =0;

     irq_desc[IRQ_EINT8_23].valid =0;

 

     irq_desc[IRQ_EINT0].valid  =0;

     irq_desc[IRQ_EINT1].valid  =0;

     irq_desc[IRQ_EINT2].valid  =0;

     irq_desc[IRQ_EINT3].valid  =0;

 

   for (irq=NORMAL_IRQ_OFFSET;irq <EXT_IRQ_OFFSET;irq++) {

 

 

     irq_desc[irq].valid =0;

     irq_desc[irq].probe_ok =1;

     irq_desc[irq].mask_ack =EINT4_23mask_ack_irq;

     irq_desc[irq].mask =EINT4_23mask_irq;

     irq_desc[irq].unmask =EINT4_23unmask_irq;

    }

   for (irq=EXT_IRQ_OFFSET;irq <SUB_IRQ_OFFSET;irq++) {

     irq_desc[irq].valid =1;

     irq_desc[irq].probe_ok =1;

     irq_desc[irq].mask_ack =SUB_mask_ack_irq;

     irq_desc[irq].mask =SUB_mask_irq;

     irq_desc[irq].unmask =SUB_unmask_irq;

    }  

}

       可以看出,初始化中断描述符表时,对EINT0、EINT1、EINT2、EINT3、EINT4_7、EINT8_23等的.valid元素复位为0,表示这几个中断不可用。结合request_irq()源码可以看出,在注册时就会出现这几个中断不能成功注册的现象,本来这几个值是1的,我真不知道开发者为何这样做!不过还得感谢他们,是他们做的这个手脚促使我学得了这么多原来不知道的东东。

       现在将这促人学习的几句注释掉,重新编译这个LINUX,重新启动这个新的内核,重新运行你的测试程序,一切都重新来,你将会兴奋的说:OK!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值