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



    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

  • 相关阅读:
    支持向量机(二)
    kafka partiton迁移方法与原理
    park和unpark
    Replicated State Machine和WAL
    thrift源码分析
    thrift使用和源码分析
    kafka源码环境搭建
    kafka指定partiton生产
    gradle构建scala
    kafka consumer代码梳理
  • 原文地址:https://www.cnblogs.com/zongzi10010/p/10003477.html
Copyright © 2020-2023  润新知