字符设备驱动学习笔记-----中断方式取得按键值

=========中断方式实现按键驱动===========================================

单片机中断方式

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_startlinux/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_startlinux/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_entrylinux/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_irqinclude/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_irqhandle_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: 0VIC 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: 4VIC 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: 9364s3c-timer S3C2410 Timer Tick

108: 0s3c-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 rootroot 64 Jan 4 05:22 0 -> /dev/console

l-wx------ 1 rootroot 64 Jan 4 05:22 1 -> /dev/console

lrwx------ 1 rootroot 64 Jan 4 05:22 10 -> /dev/tty

l-wx------ 1 rootroot 64 Jan 4 05:22 2 -> /dev/console

lr-x------ 1 rootroot 64 Jan 4 05:22 5 -> /dev/forth_button_dev

关闭中断:

[root@FriendlyARM /]# exec 5<&-

查看

[root@FriendlyARM /]# ls -l /proc/1086/fd

lr-x------ 1 rootroot 64 Jan 4 05:25 0 -> /dev/console

l-wx------ 1 rootroot 64 Jan 4 05:25 1 -> /dev/console

lrwx------ 1 rootroot 64 Jan 4 05:25 10 -> /dev/tty

l-wx------ 1 rootroot 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"},

};

//staticvolatile char key_values[]={'0','0','0','0','0','0','0','0'};

staticvolatile 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 测试文件名称

./文件名 &

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值