=========中断方式实现按键驱动===========================================
单片机中断方式 |
Linux 中断方式 |
按键按下 |
设置异常向量 |
Cpu发生中断,跳到异常赂量入口执行 |
|
异常处理函数: 保存中断现场 执许中断处理函数 恢复现场 |
early_trap_init() //构造异常向量 Memcpy() 跳转指令与在linux/arch/arm/kernel/entry-armv.S文件中定义的相对应 //以此指令为例 W(b) vector_irq + stubs_offset vector_irq 宏所作操作: __irq_usr : 保存中断现场 usr_entry 执许中断处理函数 irq_handler---> arch_irq_handler_default--> asm_do_IRQ 恢复现场 ret_to_user
|
Arm架构的cpu的异常向量地址可以是0x00000000,也可以是0xffff0000
将异常向量复制到0xffff0000处,需要使用trap_init()函数
该函数定义在linux/arch/arm/kernel/traps.c文件中,示例代码如下:
void __init early_trap_init(void)
{
#if defined(CONFIG_CPU_USE_DOMAINS)
//vectors 在配置项中定义
//查看配置项,在配置文件.config文件中可以看到
//CONFIG_VECTORS_BASE=0xffff0000
unsignedlong vectors = CONFIG_VECTORS_BASE;
#else
unsignedlong vectors = (unsigned long)vectors_page;
#endif
externchar __stubs_start[], __stubs_end[];
externchar __vectors_start[], __vectors_end[];
externchar __kuser_helper_start[], __kuser_helper_end[];
intkuser_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.
*/
//将代码段拷贝到vectors中
//__vectors_start在linux/arch/arm/kernel/entry-armv.S文件中定义
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);
/*
* Do processor specific fixups for the kuserhelpers
*/
kuser_get_tls_init(vectors);
/*
* Copy signal return handlers into the vectorpage, and
* set sigreturn to be a pointer to these.
*/
memcpy((void*)(vectors + KERN_SIGRETURN_CODE - CONFIG_VECTORS_BASE),
sigreturn_codes,sizeof(sigreturn_codes));
memcpy((void*)(vectors + KERN_RESTART_CODE - CONFIG_VECTORS_BASE),
syscall_restart_code,sizeof(syscall_restart_code));
flush_icache_range(vectors,vectors + PAGE_SIZE);
modify_domain(DOMAIN_USER,DOMAIN_CLIENT);
}
__vectors_start在linux/arch/arm/kernel/entry-armv.S文件中定义,示例代码如下:
__vectors_start:
ARM( swi SYS_ERROR0 )
THUMB( svc #0 )
THUMB( nop )
W(b) vector_und + stubs_offset
W(ldr) pc, .LCvswi + stubs_offset
W(b) vector_pabt + stubs_offset
W(b) vector_dabt + stubs_offset
W(b) vector_addrexcptn + stubs_offset
W(b) vector_irq + stubs_offset
W(b) vector_fiq + stubs_offset
.globl __vectors_end
__vectors_end:
//未定义异常分析
W(b) vector_und+ stubs_offset
/*跳转表
*Undef instr entry dispatcher
*Enter in UND mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
*/
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
/*
*Vector stubs.
*
*This code is copied to 0xffff0200 so we can use branches in the
*vectors, rather than ldr's. Note thatthis code must not
*exceed 0x300 bytes.
*
*Common stub entry macro:
* Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
*
* SPpoints to a minimal amount of processor-private memory, the address
* ofwhich is copied into r0 for the mode specific abort handler.
*/
.macro vector_stub, name, mode, correction=0
.align 5
vector_\name: //=>vector_und:
.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 remaindisabled.
@转到管理模式
mrs r0, cpsr
eor r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
msr spsr_cxsf, r0
@
@the branch table must immediately follow this code
@跳转表在上面定义
and lr, lr, #0x0f
THUMB( adr r0, 1f )
THUMB( ldr lr, [r0, lr, lsl #2] )
mov r0, sp
ARM( ldr lr,[pc, lr, lsl #2] )
movs pc, lr @branch to handler in SVC mode
ENDPROC(vector_\name) //=>ENDPROC(vector_und)
.align 2
@handler addresses follow this label
1:
.endm
//用户中断分析
W(b) vector_irq+ stubs_offset
//跳转表
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)
.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 stubs.
*
*This code is copied to 0xffff0200 so we can use branches in the
*vectors, rather than ldr's. Note thatthis code must not
*exceed 0x300 bytes.
*
*Common stub entry macro:
* Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
*
* SPpoints to a minimal amount of processor-private memory, the address
* ofwhich is copied into r0 for the mode specific abort handler.
*/
.macro vector_stub, name, mode, correction=0
.align 5
vector_\name: //=>vector_\ irq:
.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 remaindisabled.
@
mrs r0, cpsr
eor r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
msr spsr_cxsf, r0
@
@the branch table must immediately follow this code
@
and lr, lr, #0x0f
THUMB( adr r0, 1f )
THUMB( ldr lr, [r0, lr, lsl #2] )
mov r0, sp
ARM( ldr lr,[pc, lr, lsl #2] )
movs pc, lr @branch to handler in SVC mode
ENDPROC(vector_\name)
.align 2
@handler addresses follow this label
1:
.endm
查看此标号__irq_usr,示例代码如下:
__irq_usr:
usr_entry @保存寄存器的值
kuser_cmpxchg_check
get_thread_infotsk
#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
ARM( strne r0,[r0, -r0] )
THUMB( movne r0, #0 )
THUMB( strne r0, [r0] )
#endif
mov why, #0
b ret_to_user
UNWIND(.fnend )
ENDPROC(__irq_usr)
.ltorg
User_entry在linux/arch/arm/kernel/entry-armv.S 文件中定义,示例代码如下:
#if defined(CONFIG_AEABI) &&(__LINUX_ARM_ARCH__ >= 5) && (S_FRAME_SIZE & 7)
#error "sizeof(struct pt_regs) must bea multiple of 8"
#endif
.macro usr_entry
UNWIND(.fnstart )
UNWIND(.cantunwind ) @ don't unwind theuser space
sub sp, sp, #S_FRAME_SIZE
ARM( stmib sp,{r1 - r12} )
THUMB( stmia sp, {r0 - 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
@
@We are now ready to fill in the remaining blanks on the stack:
@
@ r2 - lr_<exception>, already fixed upfor correct return/restart
@ r3 - spsr_<exception>
@ r4 - orig_r0 (see pt_regs definition inptrace.h)
@
@Also, separately save sp_usr and lr_usr
@
stmia r0, {r2 - r4}
ARM( stmdb r0,{sp, lr}^ )
THUMB( store_user_sp_lrr0, r1, S_SP - S_PC )
@
@Enable the alignment trap while in kernel mode
@
alignment_trapr0
@
@Clear FP to mark the first stack frame
@
zero_fp
.endm
irq_handler示例代码如下:
/*
*Interrupt handling. Preserves r7, r8, r9
*/
.macro irq_handler
#ifdef CONFIG_MULTI_IRQ_HANDLER
ldr r5, =handle_arch_irq
mov r0, sp
ldr r5, [r5]
adr lr, BSYM(9997f)
teq r5, #0
movne pc, r5
#endif
arch_irq_handler_default //
9997:
.endm
#ifdef CONFIG_KPROBES
.section .kprobes.text,"ax",%progbits
#else
.text
#endif
arch_irq_handler_default定义在arch/arm/include/asm/entry-macro-multi.文件中,
/*
*Interrupt handling. Preserves r7, r8, r9
*/
.macro arch_irq_handler_default
get_irqnr_preambler5, lr
1: get_irqnr_and_baser0, r6, r5, lr
movne r1, sp
@
@routine called with r0 = irq number, r1 = struct pt_regs *
@
adrne lr, BSYM(1b)
bne asm_do_IRQ //异常处理函数
#ifdef CONFIG_SMP
/*
* XXX
*
* this macro assumes that irqstat (r6) andbase (r5) are
* preserved from get_irqnr_and_base above
*/
ALT_SMP(test_for_ipir0, r6, r5, lr)
ALT_UP_B(9997f)
movne r1, sp
adrne lr, BSYM(1b)
bne do_IPI
#ifdef CONFIG_LOCAL_TIMERS
test_for_ltirqr0, r6, r5, lr
movne r0, sp
adrne lr, BSYM(1b)
bne do_local_timer
#endif
#endif
9997:
.endm
.macro arch_irq_handler, symbol_name
.align 5
.global\symbol_name
\symbol_name:
mov r4, lr
arch_irq_handler_default
mov pc, r4
.endm
异常处理函数asm_do_IRQ定义在linux/arch/arm/kernel/irq.c文件中
/*
*do_IRQ handles all hardware IRQ's. Decoded IRQs should not
*come via this function. Instead, theyshould provide their
*own 'handler'
*/
asmlinkage void __exception_irq_entry
asm_do_IRQ(unsigned int irq, struct pt_regs*regs)
{
structpt_regs *old_regs = set_irq_regs(regs);
irq_enter();
/*
* Some hardware gives randomly wrong interrupts. Rather
* than crashing, do something sensible.
*/
if(unlikely(irq >= nr_irqs)) {
if (printk_ratelimit())
printk(KERN_WARNING"Bad IRQ%u\n", irq);
ack_bad_irq(irq);
}else {
generic_handle_irq(irq);
}
/*AT91 specific workaround */
irq_finish(irq);
irq_exit();
set_irq_regs(old_regs);
}
==============中断处理的框架===============================
单片机中断的处理 |
Linux 内核中中断处理 |
分辨是哪一个中断 |
asm_do_IRQ |
调用处理函数 |
|
清中断 |
|
异常处理函数asm_do_IRQ定义在linux/arch/arm/kernel/irq.c文件中
/*
*do_IRQ handles all hardware IRQ's. Decoded IRQs should not
*come via this function. Instead, theyshould provide their
*own 'handler'
*/
asmlinkage void __exception_irq_entry
asm_do_IRQ(unsigned int irq, struct pt_regs*regs)
{
structpt_regs *old_regs = set_irq_regs(regs);
irq_enter();
/*
* Some hardware gives randomly wronginterrupts. Rather
* than crashing, do something sensible.
*/
if(unlikely(irq >= nr_irqs)) {
if (printk_ratelimit())
printk(KERN_WARNING"Bad IRQ%u\n", irq);
ack_bad_irq(irq);
}else {
generic_handle_irq(irq);//调用generic_handle_irq_desc(irq, irq_to_desc(irq));
}
/*AT91 specific workaround */
irq_finish(irq);
irq_exit();
set_irq_regs(old_regs);
}
//generic_handle_irq在include/linux/irqdesc.h头文件中
static inline void generic_handle_irq(unsignedint irq)
{
generic_handle_irq_desc(irq,irq_to_desc(irq));
}
generic_handle_irq_desc(irq,irq_to_desc(irq))定义在/include/linux/irqdesc.h头文件中
static inline voidgeneric_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
{
desc->handle_irq(irq,desc); //调用指定的回调函数
}
struct irq_desc *irq_to_desc(unsigned intirq)
{
return(irq < NR_IRQS) ? irq_desc + irq : NULL;
}
handle_irq在handle_irq函数中设置,定义在linux/kernel/irq/chip.c文件中
__set_irq_handler()
__set_irq_handler()函数在set_irq_handler函数中调用,
static inline void
set_irq_handler(unsigned int irq,irq_flow_handler_t handle)
{
__set_irq_handler(irq,handle, 0, NULL);
}
arch/arm/plat-s3c64xx/irq-eint.c文件中对set_irq_handler设置,示例代码如下:
static int __inits3c64xx_init_irq_eint(void)
{
intirq;
for(irq = IRQ_EINT(0); irq <= IRQ_EINT(27); irq++) {
set_irq_chip(irq, &s3c_irq_eint);
set_irq_chip_data(irq, (void*)eint_irq_to_bit(irq));
set_irq_handler(irq, handle_level_irq);//
set_irq_flags(irq, IRQF_VALID);
}
set_irq_chained_handler(IRQ_EINT0_3,s3c_irq_demux_eint0_3);
set_irq_chained_handler(IRQ_EINT4_11,s3c_irq_demux_eint4_11);
set_irq_chained_handler(IRQ_EINT12_19,s3c_irq_demux_eint12_19);
set_irq_chained_handler(IRQ_EINT20_27,s3c_irq_demux_eint20_27);
return0;
}
handle_level_irq定义在linux/kernel/irq/chip.c文件中
/**
* handle_level_irq - Level type irq handler
* @irq: theinterrupt number
* @desc: theinterrupt description structure for this irq
*
* Level type interrupts are active as long asthe hardware line has
* the active level. This may require to maskthe interrupt and unmask
* it after the associated handler hasacknowledged the device, so the
* interrupt line is back to inactive.
*/
void
handle_level_irq(unsigned int irq, structirq_desc *desc)
{
structirqaction *action;
irqreturn_taction_ret;
raw_spin_lock(&desc->lock);
mask_ack_irq(desc);//清中断
if(unlikely(desc->status & IRQ_INPROGRESS))
goto out_unlock;
desc->status&= ~(IRQ_REPLAY | IRQ_WAITING);
kstat_incr_irqs_this_cpu(irq,desc);
/*
* If its disabled or no action available
* keep it masked and get out of here
*/
action= desc->action;
if(unlikely(!action || (desc->status & IRQ_DISABLED)))
goto out_unlock;
desc->status|= IRQ_INPROGRESS;
raw_spin_unlock(&desc->lock);
action_ret= handle_IRQ_event(irq, action); //处理中断
if(!noirqdebug)
note_interrupt(irq, desc, action_ret);
raw_spin_lock(&desc->lock);
desc->status&= ~IRQ_INPROGRESS;
if(!(desc->status & (IRQ_DISABLED | IRQ_ONESHOT)))
unmask_irq(desc);
out_unlock:
raw_spin_unlock(&desc->lock);
}
handle_IRQ_event处理函数在linux/kernel/irq/handle.c中,示例代码如下:
/**
* handle_IRQ_event - irq action chain handler
* @irq: theinterrupt number
* @action: theinterrupt action chain for this irq
*
* Handles the action chain of an irq event
*/
irqreturn_thandle_IRQ_event(unsigned int irq, struct irqaction *action)
{
irqreturn_t ret, retval = IRQ_NONE;
unsigned int status = 0;
//取得action 链表中的所有成员,执行action->handler
do {
trace_irq_handler_entry(irq,action);
ret = action->handler(irq,action->dev_id);
trace_irq_handler_exit(irq,action, ret);
switch (ret) {
caseIRQ_WAKE_THREAD:
/*
*Set result to handled so the spurious check
*does not trigger.
*/
ret = IRQ_HANDLED;
/*
*Catch drivers which return WAKE_THREAD but
*did not set up a thread function
*/
if (unlikely(!action->thread_fn)) {
warn_no_thread(irq,action);
break;
}
/*
*Wake up the handler thread for this
*action. In case the thread crashed and was
*killed we just pretend that we handled the
*interrupt. The hardirq handler above has
*disabled the device interrupt, so no irq
*storm is lurking.
*/
if (likely(!test_bit(IRQTF_DIED,
&action->thread_flags))) {
set_bit(IRQTF_RUNTHREAD,&action->thread_flags);
wake_up_process(action->thread);
}
/* Fall through to add to randomness */
case IRQ_HANDLED:
status |= action->flags;
break;
default:
break;
}
retval |= ret;
action =action->next;
} while (action);
if (status & IRQF_SAMPLE_RANDOM)
add_interrupt_randomness(irq);
local_irq_disable();
return retval;
}
注册中断request_irq.定义在include/linux/interrupt..h头文件中
static inline int __must_checkrequest_irq(unsigned int irq, // 中断号
irq_handler_t handler, //处理函数
unsigned long flags,//触发方法(上升沿/边沿)
const char *name, //中断名称
void *dev)//
{free_irq(unsigned int irq, void * dev_id)
returnrequest_threaded_irq(irq, handler, NULL, flags, name, dev);
}
request_threaded_irq在kernel/irp/manager.c文件中实现
/**
* request_threaded_irq - allocate an interruptline
* @irq: Interrupt line to allocate
* @handler: Function to be called when the IRQoccurs.
* Primary handler for threaded interrupts
* If NULL and thread_fn != NULL the default
* primary handler is installed
* @thread_fn: Function called from the irqhandler thread
* If NULL, no irq thread is created
* @irqflags: Interrupt type flags
* @devname: An ascii name for the claimingdevice
* @dev_id: A cookie passed back to the handlerfunction
*
* This call allocates interrupt resources andenables the
* interrupt line and IRQ handling. From thepoint this
* call is made your handler function may beinvoked. Since
* your handler function must clear anyinterrupt the board
* raises, you must take care both toinitialise your hardware
* and to set up the interrupt handler in theright order.
*
* If you want to set up a threaded irq handlerfor your device
* then you need to supply @handler and@thread_fn. @handler ist
* still called in hard interrupt context andhas to check
* whether the interrupt originates from thedevice. If yes it
* needs to disable the interrupt on the deviceand return
* IRQ_WAKE_THREAD which will wake up thehandler thread and run
* @thread_fn. This split handler design isnecessary to support
* shared interrupts.
*
* Dev_id must be globally unique. Normally theaddress of the
* device data structure is used as the cookie.Since the handler
* receives this value it makes sense to useit.
*
* If your interrupt is shared you must pass anon NULL dev_id
* as this is required when freeing theinterrupt.
*
* Flags:
*
* IRQF_SHARED Interrupt is shared
* IRQF_SAMPLE_RANDOM The interrupt can be used for entropy
* IRQF_TRIGGER_* Specify activeedge(s) or level
*
*/
int request_threaded_irq(unsigned int irq,irq_handler_t handler,
irq_handler_t thread_fn, unsigned longirqflags,
const char *devname, void *dev_id)
{
structirqaction *action;
structirq_desc *desc;
intretval;
/*
* Sanity-check: shared interrupts must pass ina real dev-ID,
* otherwise we'll have trouble later trying tofigure out
* which interrupt is which (messes up theinterrupt freeing
* logic etc).
*/
if((irqflags & IRQF_SHARED) && !dev_id)
return -EINVAL;
desc= irq_to_desc(irq);
if(!desc)
return -EINVAL;
if(desc->status & IRQ_NOREQUEST)
return -EINVAL;
if(!handler) {
if (!thread_fn)
return-EINVAL;
handler = irq_default_primary_handler;
}
//分配irq结构体
action= kzalloc(sizeof(struct irqaction), GFP_KERNEL);
if(!action)
return -ENOMEM;
action->handler= handler;
action->thread_fn= thread_fn;
action->flags= irqflags;
action->name= devname;
action->dev_id= dev_id;
chip_bus_lock(desc);
retval= __setup_irq(irq, desc, action); //设置irq
chip_bus_sync_unlock(desc);
if(retval)
kfree(action);
#ifdef CONFIG_DEBUG_SHIRQ_FIXME
if(!retval && (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 disable the irq to make sure that a 'real' IRQ doesn't
*run in parallel with our fake.
*/
unsigned long flags;
disable_irq(irq);
local_irq_save(flags);
handler(irq, dev_id);
local_irq_restore(flags);
enable_irq(irq);
}
#endif
returnretval;
}
__setup_irq函数,示例代码如下:
/*
*Internal function to register an irqaction - typically used to
*allocate special interrupts that are part of the architecture.
*/
static int
__setup_irq(unsigned int irq, structirq_desc *desc, struct irqaction *new)
{
structirqaction *old, **old_ptr;
constchar *old_name = NULL;
unsignedlong flags;
intnested, shared = 0;
intret;
if(!desc)
return -EINVAL;
if(desc->irq_data.chip == &no_irq_chip)
return -ENOSYS;
/*
* Some drivers like serial.c use request_irq()heavily,
* so we have to be careful not to interferewith 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);
}
/*Oneshot interrupts are not allowed with shared */
if((new->flags & IRQF_ONESHOT) && (new->flags &IRQF_SHARED))
return -EINVAL;
/*
* Check whether the interrupt nests intoanother interrupt
* thread.
*/
nested= desc->status & IRQ_NESTED_THREAD;
if(nested) {
if (!new->thread_fn)
return-EINVAL;
/*
*Replace the primary handler which was provided from
*the driver for non nested interrupt handling by the
*dummy function which warns when called.
*/
new->handler =irq_nested_primary_handler;
}
/*
* Create a handler thread when a threadfunction is supplied
* and the interrupt does not nest into anotherinterrupt
* thread.
*/
if(new->thread_fn && !nested) {
struct task_struct *t;
t = kthread_create(irq_thread, new,"irq/%d-%s", irq,
new->name);
if (IS_ERR(t))
returnPTR_ERR(t);
/*
*We keep the reference to the task struct even if
*the thread dies to avoid that the interrupt code
*references an already freed task_struct.
*/
get_task_struct(t);
new->thread = t;
}
/*
* The following block of code has to beexecuted atomically
*/
raw_spin_lock_irqsave(&desc->lock,flags);
old_ptr= &desc->action;
old= *old_ptr;
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;
gotomismatch;
}
#if defined(CONFIG_IRQ_PER_CPU)
/* All handlers must agree on per-cpuness*/
if ((old->flags & IRQF_PERCPU) !=
(new->flags & IRQF_PERCPU))
gotomismatch;
#endif
/* add new interrupt at end of irq queue*/
/*在链表中加入action*/
do {
old_ptr= &old->next;
old= *old_ptr;
} while (old);
shared = 1;
}
if(!shared) { //如果不是共享中断
irq_chip_set_defaults(desc->irq_data.chip);
init_waitqueue_head(&desc->wait_for_threads);
/* Setup the type (level, edge polarity)if configured: */
if (new->flags &IRQF_TRIGGER_MASK) {
ret= __irq_set_trigger(desc, irq,
new->flags& IRQF_TRIGGER_MASK); //引角设为中断引角
if(ret)
goto out_thread;
} else
compat_irq_chip_set_default_handler(desc);
#if defined(CONFIG_IRQ_PER_CPU)
if (new->flags & IRQF_PERCPU)
desc->status|= IRQ_PER_CPU;
#endif
desc->status &= ~(IRQ_AUTODETECT |IRQ_WAITING | IRQ_ONESHOT |
IRQ_INPROGRESS | IRQ_SPURIOUS_DISABLED);
if (new->flags & IRQF_ONESHOT)
desc->status|= IRQ_ONESHOT;
if (!(desc->status &IRQ_NOAUTOEN)) {
desc->depth= 0;
desc->status&= ~IRQ_DISABLED;
desc->irq_data.chip->irq_startup(&desc->irq_data);
} else
/*Undo nested disables: */
desc->depth= 1;
/* Exclude IRQ from balancing ifrequested */
if (new->flags & IRQF_NOBALANCING)
desc->status|= IRQ_NO_BALANCING;
/* Set default affinity mask onceeverything is setup */
setup_affinity(irq, desc);
}else if ((new->flags & IRQF_TRIGGER_MASK)
&&(new->flags & IRQF_TRIGGER_MASK)
!= (desc->status &IRQ_TYPE_SENSE_MASK)) {
/* hope the handler works with the actualtrigger mode... */
pr_warning("IRQ %d uses trigger mode%d; requested %d\n",
irq, (int)(desc->status &IRQ_TYPE_SENSE_MASK),
(int)(new->flags &IRQF_TRIGGER_MASK));
}
new->irq= irq;
*old_ptr= new;
/*Reset broken irq detection when installing new handler */
desc->irq_count= 0;
desc->irqs_unhandled= 0;
/*
* Check whether we disabled the irq via thespurious handler
* before. Reenable it and give it anotherchance.
*/
if(shared && (desc->status & IRQ_SPURIOUS_DISABLED)) {
desc->status &=~IRQ_SPURIOUS_DISABLED;
__enable_irq(desc, irq, false); //使能中断
}
raw_spin_unlock_irqrestore(&desc->lock,flags);
/*
* Strictly no need to wake it up, buthung_task complains
* when no hard interrupt wakes the thread up.
*/
if(new->thread)
wake_up_process(new->thread);
register_irq_proc(irq,desc);
new->dir= NULL;
register_handler_proc(irq,new);
return0;
mismatch:
#ifdef CONFIG_DEBUG_SHIRQ
if(!(new->flags & IRQF_PROBE_SHARED)) {
printk(KERN_ERR "IRQ handler typemismatch for IRQ %d\n", irq);
if (old_name)
printk(KERN_ERR"current handler: %s\n", old_name);
dump_stack();
}
#endif
ret= -EBUSY;
out_thread:
raw_spin_unlock_irqrestore(&desc->lock,flags);
if(new->thread) {
struct task_struct *t = new->thread;
new->thread = NULL;
if (likely(!test_bit(IRQTF_DIED,&new->thread_flags)))
kthread_stop(t);
put_task_struct(t);
}
returnret;
}
Free_irq卸载中断
移出链表
禁止中断
固定下来的中断号存放在linux/arch/arm/mach-s3c64xx/include/mach/irqs.h
查看开发板上已使用的中断
[root@FriendlyARM /]# cat /proc/interrupts
CPU0
16: 17 s3c-uart s3c6400-uart
18: 43 s3c-uart s3c6400-uart
35: 0 VIC s3c-fimc0
36: 0 VIC s3c-fimc1
40: 0 VIC s3c-g3d
41: 0 VIC s3c-vpp
42: 0 VIC s3c-rotator
43: 0 VIC s3c-g2d
44: 0 VIC TV_ENCODER
45: 0 VIC TV_SCALER
47: 0 VIC s3c-jpeg
48: 0 VIC s3c-mfc
58: 0 VIC s3c2410-wdt
62: 5771 VIC s3c-lcd
68: 48 VIC AC97
73: 0 VIC DMA
74: 0 VIC DMA
79: 18 VIC ohci_hcd:usb1
82: 4 VIC s3c2440-i2c
88: 0 VIC mmc0
89: 52 VIC mmc1
90: 0 VIC s3c-hsotg
95: 0 VIC adc
99: 240991 s3c-timer 1-wire Timer Tick
100: 9364 s3c-timer S3C2410 Timer Tick
108: 0 s3c-eint eth0
Err: 0
示例代码如下:
/****************************************
*第四个驱动程序 中断方式实现按键驱动
*****************************************/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include <mach/map.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/gpio-bank-n.h>
#include <mach/gpio-bank-l.h>
#define DEVICE_NAME"forth_button_dev"
static struct class*forth_button_dev_class;
int major;
static irqreturn_t buttons_irq(int irq,void *seen)
{
printk("irq=%d\n",irq);
returnIRQ_RETVAL(IRQ_HANDLED);
}
static int forth_button_dev_open(structinode *inode, struct file *file)
{
interr=0;
printk("forth_button_dev_open!\n");
/*注册中断*/
err=request_irq(IRQ_EINT(0),buttons_irq,IRQ_TYPE_EDGE_BOTH,"K1",(void*)0);
err=request_irq(IRQ_EINT(1),buttons_irq,IRQ_TYPE_EDGE_BOTH,"K2",(void*)1);
err=request_irq(IRQ_EINT(2),buttons_irq,IRQ_TYPE_EDGE_BOTH,"K3",(void*)2);
err=request_irq(IRQ_EINT(3),buttons_irq,IRQ_TYPE_EDGE_BOTH,"K4",(void*)3);
err=request_irq(IRQ_EINT(4),buttons_irq,IRQ_TYPE_EDGE_BOTH,"K5",(void*)4);
err=request_irq(IRQ_EINT(5),buttons_irq,IRQ_TYPE_EDGE_BOTH,"K6",(void*)5);
err=request_irq(IRQ_EINT(19),buttons_irq,IRQ_TYPE_EDGE_BOTH,"K7",(void*)6);
err=request_irq(IRQ_EINT(20),buttons_irq,IRQ_TYPE_EDGE_BOTH,"K8",(void*)7);
return0;
}
static int forth_button_dev_close(structinode *inode, struct file *file)
{
//注销中断处理程序
free_irq(IRQ_EINT(0),(void*)0);
free_irq(IRQ_EINT(1),(void *)1);
free_irq(IRQ_EINT(2),(void *)2);
free_irq(IRQ_EINT(3),(void *)3);
free_irq(IRQ_EINT(4),(void *)4);
free_irq(IRQ_EINT(5),(void *)5);
free_irq(IRQ_EINT(19),(void *)6);
free_irq(IRQ_EINT(20),(void *)7);
return0;
}
static ssize_tforth_button_dev_write(struct file *file, const char *buffer, size_t count,loff_t * ppos)
{
printk("forth_button_dev_write!\n");
return0;
}
static struct file_operationsforth_button_dev_fops = {
.owner = THIS_MODULE,
.open = forth_button_dev_open,
.release = forth_button_dev_close,
.write = forth_button_dev_write,
};
/*注册驱动程序*/
static int __initforth_button_dev_init(void){
/*major设备的主设备号,name是驱动程序的名称,fops默认的是file_operations结构*/
//如果主设备号为0,系统会自动分配
major=register_chrdev(0,DEVICE_NAME,&forth_button_dev_fops);
forth_button_dev_class= class_create(THIS_MODULE,DEVICE_NAME);
//创建设备节点
device_create(forth_button_dev_class,//
NULL,//
MKDEV(major,0),//
NULL,//
DEVICE_NAME);//
return0;
}
static void __exit forth_button_dev_exit(void){
//删除设备节点
device_destroy(forth_button_dev_class,MKDEV(major,0));
if(forth_button_dev_class){
class_destroy(forth_button_dev_class);
}
/*major和name必须和注册时的值一致*/
unregister_chrdev(major,DEVICE_NAME);
return;
}
module_init(forth_button_dev_init);
module_exit(forth_button_dev_exit);
MODULE_AUTHOR("RETACN");
MODULE_DESCRIPTION("FORTH BUTTONdriver");
MODULE_LICENSE("GPL");
下载到开发板测试,
使用命令注册中断
[root@FriendlyARM /tmp]# exec 5</dev/forth_button_dev
forth_button_dev_open!
查看中断:
[root@FriendlyARM /tmp]# cat/proc/interrupts
查看当前进程
[root@FriendlyARM /tmp]# ps
PIDUSER VSZ STAT COMMAND
1root 2992 S init
2root 0 SW [kthreadd]
3root 0 SW [ksoftirqd/0]
4root 0 SW [kworker/0:0]
5root 0 SW [kworker/u:0]
6root 0 SW< [khelper]
7root 0 SW [kworker/u:1]
345root 0 SW [sync_supers]
347root 0 SW [bdi-default]
348root 0 SW< [kblockd]
358root 0 SW [khubd]
454root 0 SW< [rpciod]
455root 0 SW [kworker/0:1]
463root 0 SW [khungtaskd]
464root 0 SW [kswapd0]
514root 0 SW [fsnotify_mark]
516root 0 SW< [aio]
526root 0 SW< [nfsiod]
530root 0 SW< [crypto]
614root 0 SW [mtdblock0]
619root 0 SW [mtdblock1]
624root 0 SW [mtdblock2]
784root 0 SW [pvrusb2-context]
905root 0 SW [yaffs-bg-1]
928root 2992 S syslogd
934root 3252 S /usr/sbin/inetd
938root 2028 S /usr/sbin/boa
941root 1504 S /usr/bin/led-player
953root 2268 S fa-network-service
954root 18600 S /opt/Qtopia/bin/qpe
955root 3316 S -/bin/sh
956root 2992 S init
960root 2992 S init
963root 2992 S init
972root 8708 S < /opt/Qtopia/bin/qss
973root 12548 S N /opt/Qtopia/bin/quicklauncher
1080root 3316 R ps
查看
[root@FriendlyARM /tmp]# ls -l /proc/955/fd
lr-x------ 1 root root 64 Jan 4 05:22 0 -> /dev/console
l-wx------ 1 root root 64 Jan 4 05:22 1 -> /dev/console
lrwx------ 1 root root 64 Jan 4 05:22 10 -> /dev/tty
l-wx------ 1 root root 64 Jan 4 05:22 2 -> /dev/console
lr-x------ 1 root root 64 Jan 4 05:22 5 -> /dev/forth_button_dev
关闭中断:
[root@FriendlyARM /]# exec 5<&-
查看
[root@FriendlyARM /]# ls -l /proc/1086/fd
lr-x------ 1 root root 64 Jan 4 05:25 0 -> /dev/console
l-wx------ 1 root root 64 Jan 4 05:25 1 -> /dev/console
lrwx------ 1 root root 64 Jan 4 05:25 10 -> /dev/tty
l-wx------ 1 root root 64 Jan 4 05:25 2 -> /dev/console
重构代码如下:
/****************************************
*第四个驱动程序 中断方式实现按键驱动
*****************************************/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include <mach/map.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/gpio-bank-n.h>
#include <mach/gpio-bank-l.h>
#define DEVICE_NAME"forth_button_dev"
static struct class*forth_button_dev_class;
int major;
staticDECLARE_WAIT_QUEUE_HEAD(button_waitq);
/*中断事件标志,中断处理函数将其置1,third_drv_read将其置0*/
static volatile int ev_press=0;
/*自定义中断结构体*/
struct button_irq_desc{
intirq;//按键中断号
intnumber;//
char*name;//按键名
};
//按键数组
static struct button_irq_descbutton_irqs[]={
{IRQ_EINT(0),0,"K0"},
{IRQ_EINT(1),1,"K1"},
{IRQ_EINT(2),2,"K2"},
{IRQ_EINT(3),3,"K3"},
{IRQ_EINT(4),4,"K4"},
{IRQ_EINT(5),5,"K5"},
{IRQ_EINT(19),6,"K6"},
{IRQ_EINT(20),7,"K7"},
};
//static volatile char key_values[]={'0','0','0','0','0','0','0','0'};
static volatile int keyValue=0;
static irqreturn_t buttons_irq(int irq,void *dev_id)
{
printk("irq=%d\n",irq);
//确定按键值
structbutton_irq_desc *button_irqs=(struct button_irq_desc *)dev_id;
intnumber;
intdown;
unsignedtmp;
number = button_irqs->number;
//检查哪个键按下
switch(number){
case0: case 1: case 2: case 3: case 4: case 5:
tmp = readl(S3C64XX_GPNDAT);
down = !(tmp & (1<<number));
break;
case6: case 7:
tmp = readl(S3C64XX_GPLDAT);
down = !(tmp & (1 << (number +5)));
break;
default:
down = 0;
}
printk("number=%d\n",number);
printk("down=%d\n",down);
/*按下down=1*/
if (down) {
keyValue=10+number;
printk("key %d down,key value= %d\n",number,keyValue);
}else{//松开
keyValue=number;
printk("key %d up,keyvalue = %d\n",number,keyValue);
}
ev_press= 1;
wake_up_interruptible(&button_waitq);
returnIRQ_RETVAL(IRQ_HANDLED);
}
static int forth_button_dev_open(structinode *inode, struct file *file)
{
printk("forth_button_dev_open!\n");
//采用中断的方式
//注册中断处理函数
inti;
interr=0;
for(i=0;i<sizeof(button_irqs)/sizeof(button_irqs[0]);i++){
if(button_irqs[i].irq<0){
continue;
}
err=request_irq(button_irqs[i].irq,buttons_irq,IRQ_TYPE_EDGE_BOTH,
button_irqs[i].name,(void*)&button_irqs[i]);
if(err)
break;
}
return0;
}
int forth_button_dev_close(struct inode*inode, struct file *file){
//注销中断处理程序
inti;
for (i = 0; i <sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
if (button_irqs[i].irq < 0) {
continue;
}
free_irq(button_irqs[i].irq, (void*)&button_irqs[i]);
}
return0;
}
static ssize_t forth_button_dev_read(structfile *file,const char __user *buf,
size_t size,loff_t * ppos)
{
if(size!=1){
return -EINVAL;
}
//如果没有按键动作发生就休眠
wait_event_interruptible(button_waitq,ev_press);
//如果有动作发生直接返回
copy_to_user(buf,&keyValue,1);
ev_press=0;
return1;
}
static struct file_operationsforth_button_dev_fops = {
.owner = THIS_MODULE,
.open = forth_button_dev_open,
.release = forth_button_dev_close,
.read = forth_button_dev_read,
};
/*注册驱动程序*/
static int __initforth_button_dev_init(void){
/*major设备的主设备号,name是驱动程序的名称,fops默认的是file_operations结构*/
//如果主设备号为0,系统会自动分配
major=register_chrdev(0,DEVICE_NAME,&forth_button_dev_fops);
forth_button_dev_class= class_create(THIS_MODULE,DEVICE_NAME);
//创建设备节点
device_create(forth_button_dev_class,//
NULL,//
MKDEV(major,0),//
NULL,//
DEVICE_NAME);//
return0;
}
static void __exitforth_button_dev_exit(void){
//删除设备节点
device_destroy(forth_button_dev_class,MKDEV(major,0));
if(forth_button_dev_class){
class_destroy(forth_button_dev_class);
}
/*major和name必须和注册时的值一致*/
unregister_chrdev(major,DEVICE_NAME);
return;
}
module_init(forth_button_dev_init);
module_exit(forth_button_dev_exit);
MODULE_AUTHOR("RETACN");
MODULE_DESCRIPTION("FORTH BUTTONdriver");
MODULE_LICENSE("GPL");
测试程序如下:
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
/*测试中断方式按键驱动*/
int main(int argc,char **argv){
intfd;
intval=1;
unsignedchar key_vals;
fd=open("/dev/forth_button_dev",O_RDWR);
if(fd<0){
printf("can not open!\n");
}
while(1){
read(fd,&key_vals,1);
printf("user key value= %d\n",key_vals);
}
return0;
}
下载到开发板
Linux 环境下使用rz
加载驱动
Insmod 驱动名.ko
使用命令方式打开驱动程序
Exec 5</dev/驱动名称
命令方式关闭驱动程序
Exec 5<&-
后台运行测试程序
修改测试程序权限
Chmod u+x 测试文件名称
./文件名 &