zoukankan      html  css  js  c++  java
  • 字符设备驱动(三)中断框架


    title: 字符设备驱动(三)中断框架
    tags: linux
    date: 2018-11-22 18:58:22
    toc: true

    字符设备驱动(三)中断框架

    引入

    mark

    裸机中断流程

    1. 外部触发
    2. CPU 发生中断, 强制的跳到异常向量处
    3. 跳转到具体函数
      1. 保存被中断处的现场(各种寄存器的值)。
      2. 处理具体任务
      3. 恢复被中断的现场

    LINUX流程

    ARM 架构的 CPU 的异常向量基址可以是 0x0000 0000,也可以是 0xffff0000,这个地址并不代表实际的内存,是虚拟地址.当建立了虚拟地址与物理地址间的映射后,得将那些异常向量,即相当于把那些跳转指令复制拷贝到这个 0xffff0000 这个地址处去。

    汇编处理

    mark

    trap_init中实现(start_kernel中调用)

    // arch/arm/kernel/traps.c
    void __init trap_init(void)
    {
    	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.
    	 */
    	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);
    }
    

    拷贝向量表

    memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
    
    1. vectors=CONFIG_VECTORS_BASE,是配置项,,在最开始的内核配置.config中定义为0xffff0000

    2. __vectors_startarch/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:
    

    向量表宏解析

    其中的vector_und等都是一个宏,搜索代码是搜索不到的.本质就是保护现场,设置管理模式,然后跳转

    	.macro	vector_stub, name, mode, correction=0
    	.align	5
    	
    vector_
    ame:
    	.if correction
    	sub	lr, lr, #correction
    	.endif
    
    	@
    	@ Save r0, lr_<exception> (parent PC) and spsr_<exception>
    	@ (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
    
    	@
    	@ the branch table must immediately follow this code
    	@
    	and	lr, lr, #0x0f
    	mov	r0, sp
    	ldr	lr, [pc, lr, lsl #2]
    	movs	pc, lr			@ branch to handler in SVC mode
    	.endm
    

    尝试展开这个宏

    	vector_stub	und, UND_MODE
    
    	.long	__und_usr			@  0 (USR_26 / USR_32)
    	.long	__und_invalid			@  1 (FIQ_26 / FIQ_32)
    	.long	__und_invalid			@  2 (IRQ_26 / IRQ_32)
    	.long	__und_svc			@  3 (SVC_26 / SVC_32)
    	.long	__und_invalid			@  4
    	.long	__und_invalid			@  5
    	.long	__und_invalid			@  6
    	.long	__und_invalid			@  7
    	.long	__und_invalid			@  8
    	.long	__und_invalid			@  9
    	.long	__und_invalid			@  a
    	.long	__und_invalid			@  b
    	.long	__und_invalid			@  c
    	.long	__und_invalid			@  d
    	.long	__und_invalid			@  e
    	.long	__und_invalid			@  f
    
    	.align	5
    

    展开如下:

    vector_und:
    
    	@
    	@ Save r0, lr_<exception> (parent PC) and spsr_<exception>
    	@ (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
    
    	@
    	@ the branch table must immediately follow this code
    	@
    	and	lr, lr, #0x0f
    	mov	r0, sp
    	ldr	lr, [pc, lr, lsl #2]
    	movs	pc, lr			@ branch to handler in SVC mode
    	
    	
    	.long	__und_usr			@  0 (USR_26 / USR_32)
    	.long	__und_invalid			@  1 (FIQ_26 / FIQ_32)
    	.long	__und_invalid			@  2 (IRQ_26 / IRQ_32)
    	.long	__und_svc			@  3 (SVC_26 / SVC_32)
    	.long	__und_invalid			@  4
    	.long	__und_invalid			@  5
    	.long	__und_invalid			@  6
    	.long	__und_invalid			@  7
    	.long	__und_invalid			@  8
    	.long	__und_invalid			@  9
    	.long	__und_invalid			@  a
    	.long	__und_invalid			@  b
    	.long	__und_invalid			@  c
    	.long	__und_invalid			@  d
    	.long	__und_invalid			@  e
    	.long	__und_invalid			@  f
    
    	.align	5
    
    

    展开下中断跳转的宏vector_irq,这里比未定义指令异常增加了先计算lr返回地址lr=lr-4

    # 	vector_stub	irq, IRQ_MODE, 4
    # 	correction=4
    
    vector_irq:
    	@.if correction
    	@sub	lr, lr, #correction
    	@.endif
    	if #4
    	sub lr,lr,#4
    	endif
    
    	@
    	@ Save r0, lr_<exception> (parent PC) and spsr_<exception>
    	@ (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
    
    	@
    	@ the branch table must immediately follow this code
    	@
    	and	lr, lr, #0x0f
    	mov	r0, sp
    	ldr	lr, [pc, lr, lsl #2]
    	movs	pc, lr			@ branch to handler in SVC mode
    
    # 跳转地址
    	.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
    

    跳转函数

    宏的最后都有一个跳转,比如中段函数跳转到__irq_usr

    __irq_usr:
    	usr_entry
    
    #ifdef CONFIG_TRACE_IRQFLAGS
    	bl	trace_hardirqs_off
    #endif
    	get_thread_info tsk
    #ifdef CONFIG_PREEMPT
    	ldr	r8, [tsk, #TI_PREEMPT]		@ get preempt count
    	add	r7, r8, #1			@ increment it
    	str	r7, [tsk, #TI_PREEMPT]
    #endif
    
    	irq_handler
    #ifdef CONFIG_PREEMPT
    	ldr	r0, [tsk, #TI_PREEMPT]
    	str	r8, [tsk, #TI_PREEMPT]
    	teq	r0, r7
    	strne	r0, [r0, -r0]
    #endif
    #ifdef CONFIG_TRACE_IRQFLAGS
    	bl	trace_hardirqs_on
    #endif
    
    	mov	why, #0
    	b	ret_to_user
    
    	.ltorg
    
    	.align	5
    

    其中usr_entry用于用户模式下发生中断时初始化中断处理堆栈,同时保存所有SVC态寄存器到堆栈。

    	.macro	usr_entry
    	sub	sp, sp, #S_FRAME_SIZE
    	stmib	sp, {r1 - r12}
    
    	ldmia	r0, {r1 - r3}
    	add	r0, sp, #S_PC		@ here for interlock avoidance
    	mov	r4, #-1			@  ""  ""     ""        ""
    
    	str	r1, [sp]		@ save the "real" r0 copied
    					@ from the exception stack
    
    #if __LINUX_ARM_ARCH__ < 6 && !defined(CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG)
    #ifndef CONFIG_MMU
    #warning "NPTL on non MMU needs fixing"
    #else
    	@ make sure our user space atomic helper is aborted
    	cmp	r2, #TASK_SIZE
    	bichs	r3, r3, #PSR_Z_BIT
    #endif
    #endif
    
    	@
    	@ We are now ready to fill in the remaining blanks on the stack:
    	@
    	@  r2 - lr_<exception>, already fixed up for correct return/restart
    	@  r3 - spsr_<exception>
    	@  r4 - orig_r0 (see pt_regs definition in ptrace.h)
    	@
    	@ Also, separately save sp_usr and lr_usr
    	@
    	stmia	r0, {r2 - r4}
    	stmdb	r0, {sp, lr}^
    
    	@
    	@ Enable the alignment trap while in kernel mode
    	@
    	alignment_trap r0
    
    	@
    	@ Clear FP to mark the first stack frame
    	@
    	zero_fp
    	.endm
    

    irq_handler也是一个宏,最终会调用asm_do_IRQ,也就是我们的处理函数

     .macro	irq_handler
     get_irqnr_preamble r5, lr
    1:	get_irqnr_and_base r0, r6, r5, lr
     movne	r1, sp
     @
     @ routine called with r0 = irq number, r1 = struct pt_regs *
     @
     adrne	lr, 1b
     bne	asm_do_IRQ
    

    C函数处理

    asm_do_IRQ

    汇编处理流程最终会进入asm_do_IRQ处理

    asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
    {
    	struct pt_regs *old_regs = set_irq_regs(regs);
    	struct irq_desc *desc = irq_desc + irq;
    
    	/*
    	 * Some hardware gives randomly wrong interrupts.  Rather
    	 * than crashing, do something sensible.
    	 */
    	if (irq >= NR_IRQS)
    		desc = &bad_irq_desc;
    
    	irq_enter();
    
    	desc_handle_irq(irq, desc);
    
    	/* AT91 specific workaround */
    	irq_finish(irq);
    
    	irq_exit();
    	set_irq_regs(old_regs);
    }
    

    irq_desc是中断函数处理的数组,最终处理在desc_handle_irq(irq, desc); desc是中断全局数组,irq中断号.

    static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc)
    {
    	desc->handle_irq(irq, desc);
    }
    

    也就是说,irq_desc是一个按照中断号为索引的结构体数组,里面存储各种信息包含中断入口函数handle_irq

    struct irq_desc {
    	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;
    	spinlock_t		lock;
    #ifdef CONFIG_SMP
    	cpumask_t		affinity;
    	unsigned int		cpu;
    #endif
    #if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE)
    	cpumask_t		pending_mask;
    #endif
    #ifdef CONFIG_PROC_FS
    	struct proc_dir_entry	*dir;
    #endif
    	const char		*name;
    } ____cacheline_internodealigned_in_smp;
    

    mark

    __set_irq_handler

    那么执行函数handle_irq具体是指向了什么?kernel/irq/chip.c中设置的desc->handle_irq = handle;

    void
    __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
    		  const char *name)
    {
    	struct irq_desc *desc;
    	unsigned long flags;
    
    	if (irq >= NR_IRQS) {
    		printk(KERN_ERR
    		       "Trying to install type control for IRQ%d
    ", irq);
    		return;
    	}
    
    	desc = irq_desc + irq;
    
    	if (!handle)
    		handle = handle_bad_irq;
    	else if (desc->chip == &no_irq_chip) {
    		printk(KERN_WARNING "Trying to install %sinterrupt handler "
    		       "for IRQ%d
    ", is_chained ? "chained " : "", irq);
    		/*
    		 * Some ARM implementations install a handler for really dumb
    		 * interrupt hardware without setting an irq_chip. This worked
    		 * with the ARM no_irq_chip but the check in setup_irq would
    		 * prevent us to setup the interrupt at all. Switch it to
    		 * dummy_irq_chip for easy transition.
    		 */
    		desc->chip = &dummy_irq_chip;
    	}
    
    	spin_lock_irqsave(&desc->lock, flags);
    
    	/* Uninstall? */
    	if (handle == handle_bad_irq) {
    		if (desc->chip != &no_irq_chip)
    			mask_ack_irq(desc, irq);
    		desc->status |= IRQ_DISABLED;
    		desc->depth = 1;
    	}
    	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->unmask(irq);
    	}
    	spin_unlock_irqrestore(&desc->lock, flags);
    }
    

    函数__set_irq_handlerset_irq_handler调用

    static inline void
    set_irq_handler(unsigned int irq, irq_flow_handler_t handle)
    {
    	__set_irq_handler(irq, handle, 0, NULL);
    }
    

    s3c24xx_init_irq

    set_irq_handler被多处调用,比如在arch/arm/plat-s3c24xx/irq.c中的s3c24xx_init_irq

    void __init s3c24xx_init_irq(void)
    
    
    ....
    		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;
    
    		case IRQ_RESERVED6:
    		case IRQ_RESERVED24:
    			/* no IRQ here */
    			break;
    
    ....
        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);
    
    ...
    	for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) {
    		irqdbf("registering irq %d (extended s3c irq)
    ", irqno);
    		set_irq_chip(irqno, &s3c_irqext_chip);
    		set_irq_handler(irqno, handle_edge_irq); ..............设置中断handler
    		set_irq_flags(irqno, IRQF_VALID);
    	}
    
    

    比如我们的外部中断0处理

    #define IRQ_EINT0      S3C2410_IRQ(0)	    /* 16 */
    for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {
    	irqdbf("registering irq %d (ext int)
    ", irqno);
    	set_irq_chip(irqno, &s3c_irq_eint0t4);
    	set_irq_handler(irqno, handle_edge_irq);
    	set_irq_flags(irqno, IRQF_VALID);
    }
    

    handle_edge_irq

    也就是填充这个全局结构数组irq_descIRQ_EINT0项中的相关信息,这里就是填充数组16~19.也就是中断最终会调用注册的handle_irq.在中断0~3中,注册了函数handle_edge_irqhandle_irq,边沿中断处理

    ///kernel/irq/chip.c
    void fastcall
    handle_edge_irq(unsigned int irq, struct irq_desc *desc)
    {
    	const unsigned int cpu = smp_processor_id();
    
    	spin_lock(&desc->lock);
    
    	desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
    
    	/*
    	 * If we're currently running this IRQ, or its disabled,
    	 * we shouldn't process the IRQ. Mark it pending, handle
    	 * the necessary masking and go out
    	 */
    	if (unlikely((desc->status & (IRQ_INPROGRESS | IRQ_DISABLED)) ||
    		    !desc->action)) {
    		desc->status |= (IRQ_PENDING | IRQ_MASKED);
    		mask_ack_irq(desc, irq);
    		goto out_unlock;
    	}
    
    	kstat_cpu(cpu).irqs[irq]++;
    
    	/* Start handling the irq */
    	desc->chip->ack(irq);
    
    	/* Mark the IRQ currently in progress.*/
    	desc->status |= IRQ_INPROGRESS;
    
    	do {
    		struct irqaction *action = desc->action;
    		irqreturn_t action_ret;
    
    		if (unlikely(!action)) {
    			desc->chip->mask(irq);
    			goto out_unlock;
    		}
    
    		/*
    		 * When another irq arrived while we were handling
    		 * one, we could have masked the irq.
    		 * Renable it, if it was not disabled in meantime.
    		 */
    		if (unlikely((desc->status &
    			       (IRQ_PENDING | IRQ_MASKED | IRQ_DISABLED)) ==
    			      (IRQ_PENDING | IRQ_MASKED))) {
    			desc->chip->unmask(irq);
    			desc->status &= ~IRQ_MASKED;
    		}
    
    		desc->status &= ~IRQ_PENDING;
    		spin_unlock(&desc->lock);
    		action_ret = handle_IRQ_event(irq, action);
    		if (!noirqdebug)
    			note_interrupt(irq, desc, action_ret);
    		spin_lock(&desc->lock);
    
    	} while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING);
    
    	desc->status &= ~IRQ_INPROGRESS;
    out_unlock:
    	spin_unlock(&desc->lock);
    }
    

    desc->chip->ack(irq);是之前设置的set_irq_chip(irqno, &s3c_irq_eint0t4);查找结构s3c_irq_eint0t4,具体也就是清中断了

    static struct irq_chip s3c_irq_eint0t4 = {
    	.name		= "s3c-ext0",
    	.ack		= s3c_irq_ack,
    	.mask		= s3c_irq_mask,
    	.unmask		= s3c_irq_unmask,
    	.set_wake	= s3c_irq_wake,
    	.set_type	= s3c_irqext_type,
    };
    static inline void
    s3c_irq_ack(unsigned int irqno)
    {
    	unsigned long bitval = 1UL << (irqno - IRQ_EINT0);
    
    	__raw_writel(bitval, S3C2410_SRCPND);
    	__raw_writel(bitval, S3C2410_INTPND);
    }
    static inline void
    s3c_irq_ack(unsigned int irqno)
    {
    	unsigned long bitval = 1UL << (irqno - IRQ_EINT0);
    
    	__raw_writel(bitval, S3C2410_SRCPND);
    	__raw_writel(bitval, S3C2410_INTPND);
    }
    extern void		__raw_writel(u32 b, volatile void __iomem *addr);
    

    handle_IRQ_event

    接下来重点函数handle_IRQ_event,链表操作,取出action的链表成员,执行action->handle

    struct irqaction *action = desc->action;
    action_ret = handle_IRQ_event(irq, action);
    

    desc->action

    接着去分析action这个链表,它是desc的一个成员

    struct irq_desc {
        irq_flow_handler_t	handle_irq;
    	struct irq_chip		*chip;   //底层芯片相关的函数,清中断,开关使能等
        struct irqaction	*action;	/* IRQ action list */
        ...
    }    
    struct irqaction {
    	irq_handler_t handler;
    	unsigned long flags;
    	cpumask_t mask;
    	const char *name;
    	void *dev_id;
    	struct irqaction *next;
    	int irq;
    	struct proc_dir_entry *dir;
    };
    

    mark

    request_irq

    action函数链表由request_irq设置kernel/irq/manage.c,完成了中断引脚的设置使能等

    irq:中断号
    handler:处理函数
    irqflags:上升沿触发,下降沿触发,边沿触发等。指定了快速中断或中断共享等中断处理属性.
    *devname:中断名字。通常是设备驱动程序的名称。改值用在 /proc/interrupt 系统 (虚拟)
    文件上,或内核发生中断错误时使用。
    dev_id 可作为共享中断时的中断区别参数,也可以用来指定中断服务函数需要参考的数据地址。也用于卸载action
    
    int request_irq(unsigned int irq, irq_handler_t handler,
    		unsigned long irqflags, const char *devname, void *dev_id)
    {
    	struct irqaction *action;
    	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;
    	if (irq >= NR_IRQS)
    		return -EINVAL;
    	if (irq_desc[irq].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;
    
    	select_smp_affinity(irq);
    
    #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 do this before actually registering it, to make sure that
    		 * a 'real' IRQ doesn't run in parallel with our fake
    		 */
    		if (irqflags & IRQF_DISABLED) {
    			unsigned long flags;
    
    			local_irq_save(flags);
    			handler(irq, dev_id);
    			local_irq_restore(flags);
    		} else
    			handler(irq, dev_id);
    	}
    #endif
    
    	retval = setup_irq(irq, action);
    	if (retval)
    		kfree(action);
    
    	return retval;
    }
    

    这里分配一个action成员,将传递的参数赋值到action的成员然后使用setup_irq(irq, action);设置

    setup_irq

    判断是否为共享中断,所谓共享中断也就是中断源一样的中断,共享中断只能加入共享中断的链表中.接着设置引脚等

    int setup_irq(unsigned int irq, struct irqaction *new)
    {
    	struct irq_desc *desc = irq_desc + irq;
    	struct irqaction *old, **p;
    	const char *old_name = NULL;
    	unsigned long flags;
    	int shared = 0;
    
    	if (irq >= NR_IRQS)
    		return -EINVAL;
    
    	if (desc->chip == &no_irq_chip)
    		return -ENOSYS;
    	/*
    	 * Some drivers like serial.c use request_irq() heavily,
    	 * so we have to be careful not to interfere with a
    	 * running system.
    	 */
    	if (new->flags & IRQF_SAMPLE_RANDOM) {
    		/*
    		 * This function might sleep, we want to call it first,
    		 * outside of the atomic block.
    		 * Yes, this might clear the entropy pool if the wrong
    		 * driver is attempted to be loaded, without actually
    		 * installing a new handler, but is this really a problem,
    		 * only the sysadmin is able to do this.
    		 */
    		rand_initialize_irq(irq);
    	}
    
    	/*
    	 * The following block of code has to be executed atomically
    	 */
    	spin_lock_irqsave(&desc->lock, flags);
    	p = &desc->action;
    	old = *p;
    	if (old) {
    		/*
    		 * Can't share interrupts unless both agree to and are
    		 * the same type (level, edge, polarity). So both flag
    		 * fields must have IRQF_SHARED set and the bits which
    		 * set the trigger type must match.
    		 */
    		if (!((old->flags & new->flags) & IRQF_SHARED) ||
    		    ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK)) {
    			old_name = old->name;
    			goto mismatch;
    		}
    
    #if defined(CONFIG_IRQ_PER_CPU)
    		/* All handlers must agree on per-cpuness */
    		if ((old->flags & IRQF_PERCPU) !=
    		    (new->flags & IRQF_PERCPU))
    			goto mismatch;
    #endif
    
    		/* add new interrupt at end of irq queue */
    		do {
    			p = &old->next;
    			old = *p;
    		} while (old);
    		shared = 1;
    	}
    
    	*p = new;
    
    	/* Exclude IRQ from balancing */
    	if (new->flags & IRQF_NOBALANCING)
    		desc->status |= IRQ_NO_BALANCING;
    
    	if (!shared) {
    		irq_chip_set_defaults(desc->chip);
    
    #if defined(CONFIG_IRQ_PER_CPU)
    		if (new->flags & IRQF_PERCPU)
    			desc->status |= IRQ_PER_CPU;
    #endif
    
    		/* Setup the type (level, edge polarity) if configured: */
    		if (new->flags & IRQF_TRIGGER_MASK) {
    			if (desc->chip && desc->chip->set_type)
    				desc->chip->set_type(irq,
    						new->flags & IRQF_TRIGGER_MASK);
    			else
    				/*
    				 * IRQF_TRIGGER_* but the PIC does not support
    				 * multiple flow-types?
    				 */
    				printk(KERN_WARNING "No IRQF_TRIGGER set_type "
    				       "function for IRQ %d (%s)
    ", irq,
    				       desc->chip ? desc->chip->name :
    				       "unknown");
    		} else
    			compat_irq_chip_set_default_handler(desc);
    
    		desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING |
    				  IRQ_INPROGRESS);
    
    		if (!(desc->status & IRQ_NOAUTOEN)) {
    			desc->depth = 0;
    			desc->status &= ~IRQ_DISABLED;
    			if (desc->chip->startup)
    				desc->chip->startup(irq);
    			else
    				desc->chip->enable(irq);
    		} else
    			/* Undo nested disables: */
    			desc->depth = 1;
    	}
    	/* Reset broken irq detection when installing new handler */
    	desc->irq_count = 0;
    	desc->irqs_unhandled = 0;
    	spin_unlock_irqrestore(&desc->lock, flags);
    
    	new->irq = irq;
    	register_irq_proc(irq);
    	new->dir = NULL;
    	register_handler_proc(irq, new);
    
    	return 0;
    
    mismatch:
    #ifdef CONFIG_DEBUG_SHIRQ
    	if (!(new->flags & IRQF_PROBE_SHARED)) {
    		printk(KERN_ERR "IRQ handler type mismatch for IRQ %d
    ", irq);
    		if (old_name)
    			printk(KERN_ERR "current handler: %s
    ", old_name);
    		dump_stack();
    	}
    #endif
    	spin_unlock_irqrestore(&desc->lock, flags);
    	return -EBUSY;
    }
    
    

    这里desc->chip->set_type调用设置中断的引脚寄存器等,是在初始化中s3c24xx_init_irq赋值过的chip,通过request_irq传递的flag指定触发模式等

    static struct irq_chip s3c_irqext_chip = {
    	.name		= "s3c-ext",
    	.mask		= s3c_irqext_mask,
    	.unmask		= s3c_irqext_unmask,
    	.ack		= s3c_irqext_ack,
    	.set_type	= s3c_irqext_type,
    	.set_wake	= s3c_irqext_wake
    };
    
    static struct irq_chip s3c_irq_eint0t4 = {
    	.name		= "s3c-ext0",
    	.ack		= s3c_irq_ack,
    	.mask		= s3c_irq_mask,
    	.unmask		= s3c_irq_unmask,
    	.set_wake	= s3c_irq_wake,
    	.set_type	= s3c_irqext_type,
    };
    
    int
    s3c_irqext_type(unsigned int irq, unsigned int type)
    {
    	void __iomem *extint_reg;
    	void __iomem *gpcon_reg;
    	unsigned long gpcon_offset, extint_offset;
    	unsigned long newvalue = 0, value;
    
    	if ((irq >= IRQ_EINT0) && (irq <= IRQ_EINT3))
    	{
    		gpcon_reg = S3C2410_GPFCON;
    		extint_reg = S3C24XX_EXTINT0;
    		gpcon_offset = (irq - IRQ_EINT0) * 2;
    		extint_offset = (irq - IRQ_EINT0) * 4;
    	}
    	else if ((irq >= IRQ_EINT4) && (irq <= IRQ_EINT7))
    	{
    		gpcon_reg = S3C2410_GPFCON;
    		extint_reg = S3C24XX_EXTINT0;
    		gpcon_offset = (irq - (EXTINT_OFF)) * 2;
    		extint_offset = (irq - (EXTINT_OFF)) * 4;
    	}
    	else if ((irq >= IRQ_EINT8) && (irq <= IRQ_EINT15))
    	{
    		gpcon_reg = S3C2410_GPGCON;
    		extint_reg = S3C24XX_EXTINT1;
    		gpcon_offset = (irq - IRQ_EINT8) * 2;
    		extint_offset = (irq - IRQ_EINT8) * 4;
    	}
    	else if ((irq >= IRQ_EINT16) && (irq <= IRQ_EINT23))
    	{
    		gpcon_reg = S3C2410_GPGCON;
    		extint_reg = S3C24XX_EXTINT2;
    		gpcon_offset = (irq - IRQ_EINT8) * 2;
    		extint_offset = (irq - IRQ_EINT16) * 4;
    	} else
    		return -1;
    
    	/* Set the GPIO to external interrupt mode */
    	value = __raw_readl(gpcon_reg);
    	value = (value & ~(3 << gpcon_offset)) | (0x02 << gpcon_offset);
    	__raw_writel(value, gpcon_reg);
    
    	/* Set the external interrupt to pointed trigger type */
    	switch (type)
    	{
    		case IRQT_NOEDGE:
    			printk(KERN_WARNING "No edge setting!
    ");
    			break;
    
    		case IRQT_RISING:
    			newvalue = S3C2410_EXTINT_RISEEDGE;
    			break;
    
    		case IRQT_FALLING:
    			newvalue = S3C2410_EXTINT_FALLEDGE;
    			break;
    
    		case IRQT_BOTHEDGE:
    			newvalue = S3C2410_EXTINT_BOTHEDGE;
    			break;
    
    		case IRQT_LOW:
    			newvalue = S3C2410_EXTINT_LOWLEV;
    			break;
    
    		case IRQT_HIGH:
    			newvalue = S3C2410_EXTINT_HILEV;
    			break;
    
    		default:
    			printk(KERN_ERR "No such irq type %d", type);
    			return -1;
    	}
    
    	value = __raw_readl(extint_reg);
    	value = (value & ~(7 << extint_offset)) | (newvalue << extint_offset);
    	__raw_writel(value, extint_reg);
    
    	return 0;
    }
    

    free_irq

    关闭中断,也就是取消中断处理函数,在全局数组irq_desc寻找到对应的action链表,通过id将其从链表中删除.如果链表空了,则禁止中断

    void free_irq(unsigned int irq, void *dev_id)
    

    小结

    流程

    1. start_kernel中调用trap_init 拷贝向量表到 0xffff0000
    2. 触发中断,进入中断向量表
    3. vector_irq跳转进入保存现场,跳转到__irq_usr
    4. __irq_usr调用usr_entry进行用户模式下发生中断时初始化中断处理堆栈,通过irq_handler调用asm_do_IRQ进入C函数处理
    5. asm_do_IRQ寻找irq_desc 数组,调用desc->handle_irq来执行irq_desc 数组中的入口函数handle_irq,这里外中断的话执行的是handle_edge_irq
    6. 中断入口函数清中断,执行action中的链表函数
    7. action中会判断是否为共享中断,设置中断类型,引脚寄存器等,同时开启中断
    8. 使用free_irq 卸载掉链表中的函数,如果链表函数空了则关中断

    设置irq_desc 数组

    1. s3c24xx_init_irq中进行数组的初始化
    2. set_irq_chip初始化数组中的chip结构
    3. set_irq_handler设置中断入口函数handle_irq
    4. request_irq来创建action链表,分配空间,并加入链表
    5. struct irq_chip *chip; 这是底层芯片相关的函数,清中断,开关使能等

    关键结构成员

    struct irq_desc {
    	irq_flow_handler_t	handle_irq;	//中断入口函数,在外中断指向了 handle_edge_irq
    	struct irq_chip		*chip;		//芯片底层处理函数,指向s3c_irqex_chip
    	struct irqaction	*action;	//具体执行函数的链表,会被handle_irq 入口函数使用的
    }
    
    
    struct irq_chip {
    	const char	*name;
    	unsigned int	(*startup)(unsigned int irq);	//启动中断
    	void		(*shutdown)(unsigned int irq);		//关闭中断
    	void		(*enable)(unsigned int irq);		//使能中断
    	void		(*disable)(unsigned int irq);		//禁止中断
    
    	void		(*ack)(unsigned int irq);			//响应中断,清中断
    }
    
    struct irqaction {
    	irq_handler_t handler;							//action的执行函数
    	unsigned long flags;							//标志,用于设置中断边沿等
    };
    
    

    mark

    完整框架图

    mark

    流程一览

    mark

    设置向量表

    mark

    设置全局中断数组

    mark

    设置用户action动作

    mark

  • 相关阅读:
    LeetCode 295. Find Median from Data Stream (堆)
    LeetCode 292. Nim Game(博弈论)
    《JavaScript 模式》读书笔记(4)— 函数2
    《JavaScript 模式》读书笔记(4)— 函数1
    《JavaScript 模式》读书笔记(3)— 字面量和构造函数3
    《JavaScript 模式》读书笔记(3)— 字面量和构造函数2
    《JavaScript 模式》读书笔记(3)— 字面量和构造函数1
    《JavaScript 模式》读书笔记(2)— 基本技巧3
    《JavaScript 模式》读书笔记(2)— 基本技巧2
    《JavaScript 模式》读书笔记(2)— 基本技巧1
  • 原文地址:https://www.cnblogs.com/zongzi10010/p/10003477.html
Copyright © 2011-2022 走看看