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

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

单片机中断方式

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

./文件名 &

你可能感兴趣的:(字符设备驱动学习笔记-----中断方式取得按键值)