linux中断子系统(一) - 注册系统中断处理函数
2015-10-07 14:11
495 查看
声明:本博内容由/article/8286455.html原创,转载请注明出处,谢谢!
找到宏MACHINE_START定义原型在arch/arm/include/asm/mach/arch.h中
可以看出,所有的machine_desc结构体都处于”.arch.info.init”段中,根据链接脚本arch/arm/kernel/vmlinux.lds.S,这些machine_desc结构体被链接到起始地址为__arch_info_begin,结束地址为__arch_info_end的地方。
对于系统中断的注册,基本就3个函数,后面有文章会详细描述:
set_irq_chip
set_irq_handler
set_irq_chained_handler
以上已经注册了从EINT0开始到INT_ADC的所有中断的系统中断处理函数,另外因为EINT4_7, EINT8_23, INT_UART0, INT_UART1, INT_UART2, INT_ADC还包含子中断,所以他们除了本身要设置系统中断处理函数外,还要设置级联中断处理函数和子中断的系统中断处理函数。
可以看出
a. 对于单一中断,系统中断处理函数注册的一般形式为:
set_irq_chip(irqno, &s3c_irq_chip);
set_irq_handler(irqno, handle_edge_irq);
set_irq_flags(irqno, IRQF_VALID);
b. 对于包含子中断的情况,系统中断处理函数的注册的一般形式:
1) 父中断本身要设置系统中断处理函数
set_irq_chip(IRQ_UART0, &s3c_irq_level_chip);
set_irq_handler(IRQ_UART0, handle_level_irq);
2) 父中断设置级联中断处理函数
set_irq_chained_handler(IRQ_UART0, s3c_irq_demux_uart0);
3) 子中断设置系统中断处理函数
for (irqno = IRQ_S3CUART_RX0; irqno <= IRQ_S3CUART_ERR0; irqno++) {
set_irq_chip(irqno, &s3c_irq_uart0);
set_irq_handler(irqno, handle_level_irq);
set_irq_flags(irqno, IRQF_VALID);
}
实际上,b中step2设置级联中断处理函数已经将b中step1设置的系统中断处理函数给覆盖掉了,他们都是赋值给desc->handle_irq的。所以当父中断来了之后,首先执行的是级联中断处理函数,在级联中断处理函数中根据子中断的情况进行分发,执行对应的子中断系统处理函数。
函数指针void (*init_arch_irq)(void) __initdata = NULL;
在setup_arch函数中被初始化为mdesc->init_irq(所以这个init_arch_irq归根到底就是上面的s3c24xx_init_irq),在init_IRQ函数中被调用。
至于在setup_arch函数中lookup_machine_type如何找到特定的struct machine_desc的呢? 在arch/arm/kernel/head-common.S中定义:
根据uboot中传来的在r1寄存器中的机器类型ID和链接在.arch.info.init段中的machine_desc结构中的成员nr作比较,如果相等,表示找到了匹配的machine_desc结构,于是返回machine_desc的地址(存于r5中),如果__arch_info_begin到__arch_info_end之间的所有nr成员都不等于r1寄存器,则返回0(存于r5中)。
重要的宏
内核中使用宏MACHINE_START、MACHINE_END来定义一个machine_desc结构,machine_desc中定义了:机器类型,起始I/O物理地址,bootloader传入的参数地址,中断初始化函数,I/O映射函数等,在文件arch/arm/mach-s3c2440/mach-mini2440.c中MACHINE_START(MINI2440, "FriendlyARM Mini2440 development board") .phys_io = S3C2410_PA_UART, .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc, .boot_params = S3C2410_SDRAM_PA + 0x100, .init_irq = s3c24xx_init_irq, .map_io = mini2440_map_io, .init_machine = mini2440_machine_init, .timer = &s3c24xx_timer, MACHINE_END
找到宏MACHINE_START定义原型在arch/arm/include/asm/mach/arch.h中
/* * Set of macros to define architecture features. This is built into * a table by the linker. */ #define MACHINE_START(_type,_name) \ static const struct machine_desc __mach_desc_##_type \ __used \ __attribute__((__section__(".arch.info.init"))) = { \ .nr = MACH_TYPE_##_type, \ .name = _name, #define MACHINE_END \ };
可以看出,所有的machine_desc结构体都处于”.arch.info.init”段中,根据链接脚本arch/arm/kernel/vmlinux.lds.S,这些machine_desc结构体被链接到起始地址为__arch_info_begin,结束地址为__arch_info_end的地方。
s3c24xx_init_irq
MACHINE_SATRT和MACHINE_END宏定义中有个初始化中断的函数s3c24xx_init_irq,定义在arch/arm/plat-s3c24xx/irq.c文件中void __init s3c24xx_init_irq(void) { /* first, clear all interrupts pending... */ ... /* register the main interrupts */ for (irqno = IRQ_EINT4t7; irqno <= IRQ_ADCPARENT; irqno++) { /* set all the s3c2410 internal irqs */ switch (irqno) { /* deal with the special IRQs (cascaded) */ case IRQ_EINT4t7: case IRQ_EINT8t23: case IRQ_UART0: case IRQ_UART1: case IRQ_UART2: case IRQ_ADCPARENT: set_irq_chip(irqno, &s3c_irq_level_chip); set_irq_handler(irqno, handle_level_irq); break; default: set_irq_chip(irqno, &s3c_irq_chip); set_irq_handler(irqno, handle_edge_irq); set_irq_flags(irqno, IRQF_VALID); } } /* setup the cascade irq handlers */ set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint4t7); set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8); set_irq_chained_handler(IRQ_UART0, s3c_irq_demux_uart0); set_irq_chained_handler(IRQ_UART1, s3c_irq_demux_uart1); set_irq_chained_handler(IRQ_UART2, s3c_irq_demux_uart2); set_irq_chained_handler(IRQ_ADCPARENT, s3c_irq_demux_adc); /* external interrupts */ for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) { irqdbf("registering irq %d (ext int)\n", irqno); set_irq_chip(irqno, &s3c_irq_eint0t4); set_irq_handler(irqno, handle_edge_irq); set_irq_flags(irqno, IRQF_VALID); } for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) { irqdbf("registering irq %d (extended s3c irq)\n", irqno); set_irq_chip(irqno, &s3c_irqext_chip); set_irq_handler(irqno, handle_edge_irq); set_irq_flags(irqno, IRQF_VALID); } /* register the uart interrupts */ irqdbf("s3c2410: registering external interrupts\n"); for (irqno = IRQ_S3CUART_RX0; irqno <= IRQ_S3CUART_ERR0; irqno++) { irqdbf("registering irq %d (s3c uart0 irq)\n", irqno); set_irq_chip(irqno, &s3c_irq_uart0); set_irq_handler(irqno, handle_level_irq); set_irq_flags(irqno, IRQF_VALID); } for (irqno = IRQ_S3CUART_RX1; irqno <= IRQ_S3CUART_ERR1; irqno++) { irqdbf("registering irq %d (s3c uart1 irq)\n", irqno); set_irq_chip(irqno, &s3c_irq_uart1); set_irq_handler(irqno, handle_level_irq); set_irq_flags(irqno, IRQF_VALID); } for (irqno = IRQ_S3CUART_RX2; irqno <= IRQ_S3CUART_ERR2; irqno++) { irqdbf("registering irq %d (s3c uart2 irq)\n", irqno); set_irq_chip(irqno, &s3c_irq_uart2); set_irq_handler(irqno, handle_level_irq); set_irq_flags(irqno, IRQF_VALID); } for (irqno = IRQ_TC; irqno <= IRQ_ADC; irqno++) { irqdbf("registering irq %d (s3c adc irq)\n", irqno); set_irq_chip(irqno, &s3c_irq_adc); set_irq_handler(irqno, handle_edge_irq); set_irq_flags(irqno, IRQF_VALID); } irqdbf("s3c2410: registered interrupt handlers\n"); }
对于系统中断的注册,基本就3个函数,后面有文章会详细描述:
set_irq_chip
set_irq_handler
set_irq_chained_handler
以上已经注册了从EINT0开始到INT_ADC的所有中断的系统中断处理函数,另外因为EINT4_7, EINT8_23, INT_UART0, INT_UART1, INT_UART2, INT_ADC还包含子中断,所以他们除了本身要设置系统中断处理函数外,还要设置级联中断处理函数和子中断的系统中断处理函数。
可以看出
a. 对于单一中断,系统中断处理函数注册的一般形式为:
set_irq_chip(irqno, &s3c_irq_chip);
set_irq_handler(irqno, handle_edge_irq);
set_irq_flags(irqno, IRQF_VALID);
b. 对于包含子中断的情况,系统中断处理函数的注册的一般形式:
1) 父中断本身要设置系统中断处理函数
set_irq_chip(IRQ_UART0, &s3c_irq_level_chip);
set_irq_handler(IRQ_UART0, handle_level_irq);
2) 父中断设置级联中断处理函数
set_irq_chained_handler(IRQ_UART0, s3c_irq_demux_uart0);
3) 子中断设置系统中断处理函数
for (irqno = IRQ_S3CUART_RX0; irqno <= IRQ_S3CUART_ERR0; irqno++) {
set_irq_chip(irqno, &s3c_irq_uart0);
set_irq_handler(irqno, handle_level_irq);
set_irq_flags(irqno, IRQF_VALID);
}
实际上,b中step2设置级联中断处理函数已经将b中step1设置的系统中断处理函数给覆盖掉了,他们都是赋值给desc->handle_irq的。所以当父中断来了之后,首先执行的是级联中断处理函数,在级联中断处理函数中根据子中断的情况进行分发,执行对应的子中断系统处理函数。
系统起来何时调用s3c24xx_init_irq
start_kernel setup_arch(&command_line); (arch/arm/kernel/setup.c) mdesc = setup_machine(machine_arch_type); lookup_machine_type//寻找特定mdesc init_arch_irq = mdesc->init_irq;//使用mdesc->init_irq初始化函数指针init_arch_irq ... early_irq_init (kernel/irq/handle.c) init_IRQ (与特定体系结构相关,在arch/arm/kernel/irq.c中) init_arch_irq
函数指针void (*init_arch_irq)(void) __initdata = NULL;
在setup_arch函数中被初始化为mdesc->init_irq(所以这个init_arch_irq归根到底就是上面的s3c24xx_init_irq),在init_IRQ函数中被调用。
至于在setup_arch函数中lookup_machine_type如何找到特定的struct machine_desc的呢? 在arch/arm/kernel/head-common.S中定义:
__lookup_machine_type: adr r3, 4b ldmia r3, {r4, r5, r6} sub r3, r3, r4 @ get offset between virt&phys add r5, r5, r3 @ convert virt addresses to add r6, r6, r3 @ physical address space 1: ldr r3, [r5, #MACHINFO_TYPE] @ get machine type teq r3, r1 @ matches loader number? beq 2f @ found add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc cmp r5, r6 blo 1b mov r5, #0 @ unknown machine 2: mov pc, lr ENDPROC(__lookup_machine_type)
根据uboot中传来的在r1寄存器中的机器类型ID和链接在.arch.info.init段中的machine_desc结构中的成员nr作比较,如果相等,表示找到了匹配的machine_desc结构,于是返回machine_desc的地址(存于r5中),如果__arch_info_begin到__arch_info_end之间的所有nr成员都不等于r1寄存器,则返回0(存于r5中)。
相关文章推荐
- Linux文件系统以及目录结构简介
- Linux 运维工程师的十个基本技能点
- Centos下Yum安装PHP5.5,5.6
- linux安装svn
- Linux系统上低功耗蓝牙遥控器开发项目笔记
- OK335xS pwm buzzer Linux driver hacking
- Linux标准输入输出
- Linux实现四则运算的简单方法
- LinuxMint下tty.js的安装指南
- 获知Linux的进程运行在哪个CPU内核上的方法
- linux下ftp上传
- 使用 Xmanager 远程连接到 CentOS 7
- Install php-mcrypt on CentOS 6
- linux 关机和重启命令
- Linux启动过程详解
- Linux块设备驱动之NOR FLASH
- 安装CentOS-6.7
- 持续更新:Centos常用方便的命令与技巧集合
- 图解Linux命令之--cd命令
- linux下motion摄像头监控编译与配置