韦东山第12课-字符设备、中断方式查询驱动

2019-07-13 08:07发布

1 裸板中断方式查询按键

(详情看嵌入式linux应用开发完全手册 P143) 源码见:百问网JZ2440v2主光盘hardwareint 疑问点:期间有很多基础器没有设置说是用的默认配置,但是技术手册中并没有看到默认设置。 比如说你要设置EINT0为中断触发功能,在技术手册中,将设计到EINT0的寄存器设置一遍,内容看一遍就会了。 中断模式图:
最主要代码: //head.S @****************************************************************************** @ File:head.S @ 功能:初始化,设置中断模式、管理模式的栈,设置好中断处理函数 @****************************************************************************** .extern main .text .global _start _start: @****************************************************************************** @ 中断向量,本程序中,除Reset和HandleIRQ外,其它异常都没有使用 @****************************************************************************** b Reset @ 0x04: 炊ㄒ逯噶钪兄鼓J降南蛄康 HandleUndef: b HandleUndef @ 0x08: 管理模式的向量地址,通过SWI指令进入此模式 HandleSWI: b HandleSWI @ 0x0c: 指令预取终止导致的异常的向量地址 HandlePrefetchAbort: b HandlePrefetchAbort @ 0x10: 数据访问终止导致的异常的向量地址 HandleDataAbort: b HandleDataAbort @ 0x14: 保留 HandleNotUsed: b HandleNotUsed @ 0x18: 中断模式的向量地址 b HandleIRQ @ 0x1c: 快中断模式的向量地址 HandleFIQ: b HandleFIQ Reset: ldr sp, =4096 @ 设置栈指针,以下都是C函数,调用前需要设好栈 bl disable_watch_dog @ 关闭WATCHDOG,否则CPU会不断重启 msr cpsr_c, #0xd2 @ 进入中断模式 ldr sp, =3072 @ 设置中断模式栈指针 msr cpsr_c, #0xd3 @ 进入管理模式 ldr sp, =4096 @ 设置管理模式栈指针, @ 其实复位之后,CPU就处于管理模式, @ 前面的“ldr sp, =4096”完成同样的功能,此句可省略 bl init_led @ 始化LED的GPIO管脚 bl init_irq @ 用中断初始化函数,在init.c中 msr cpsr_c, #0x5f @ 置I-bit=0,开IRQ中断 ldr lr, =halt_loop @ 置返回地址 ldr pc, =main @ 用main函数 halt_loop: b halt_loop HandleIRQ: sub lr, lr, #4 @ 计算返回地址 stmdb sp!, { r0-r12,lr } @ 保存使用到的寄存器 @ 注意,此时的sp是中断模式的sp @ 初始值是上面设置的3072 ldr lr, =int_return @ 设置调用ISR即EINT_Handle函数后的返回地址 ldr pc, =EINT_Handle @ 调用中断服务函数,在interrupt.c中 int_return: ldmia sp!, { r0-r12,pc }^ @ 中断返回, ^表示将spsr的值复制到cpsr
// init.c
/* * init.c: 进行一些初始化 */ #include "s3c24xx.h" /* * LED1,LED2,LED4对应GPF4、GPF5、GPF6 */ #define GPF4_out (1<<(4*2)) #define GPF5_out (1<<(5*2)) #define GPF6_out (1<<(6*2)) #define GPF4_msk (3<<(4*2)) #define GPF5_msk (3<<(5*2)) #define GPF6_msk (3<<(6*2)) /* * S2,S3,S4对应GPF0、GPF2、GPG3 */ #define GPF0_eint (0x2<<(0*2)) #define GPF2_eint (0x2<<(2*2)) #define GPG3_eint (0x2<<(3*2)) #define GPF0_msk (3<<(0*2)) #define GPF2_msk (3<<(2*2)) #define GPG3_msk (3<<(3*2)) /* * 关闭WATCHDOG,否则CPU会不断重启 */ void disable_watch_dog(void) { WTCON = 0; // 关闭WATCHDOG很简单,往这个寄存器写0即可 } void init_led(void) { // LED1,LED2,LED4对应的3根引脚设为输出 GPFCON &= ~(GPF4_msk | GPF5_msk | GPF6_msk); GPFCON |= GPF4_out | GPF5_out | GPF6_out; } /* * 初始化GPIO引脚为外部中断 * GPIO引脚用作外部中断时,默认为低电平触发、IRQ方式(不用设置INTMOD) */ void init_irq( ) { // S2,S3对应的2根引脚设为中断引脚 EINT0,ENT2 GPFCON &= ~(GPF0_msk | GPF2_msk); GPFCON |= GPF0_eint | GPF2_eint; // S4对应的引脚设为中断引脚EINT11 GPGCON &= ~GPG3_msk; GPGCON |= GPG3_eint; // 对于EINT11,需要在EINTMASK寄存器中使能它 EINTMASK &= ~(1<<11); /* * 设定优先级: * ARB_SEL0 = 00b, ARB_MODE0 = 0: REQ1 > REQ3,即EINT0 > EINT2 * 仲裁器1、6无需设置 * 最终: * EINT0 > EINT2 > EINT11即K2 > K3 > K4 */ PRIORITY = (PRIORITY & ((~0x01) | (0x3<<7))) | (0x0 << 7) ; // EINT0、EINT2、EINT8_23使能 INTMSK &= (~(1<<0)) & (~(1<<2)) & (~(1<<5)); } //interrupt.c#include "s3c24xx.h" void EINT_Handle() { unsigned long oft = INTOFFSET; unsigned long val; switch( oft ) { // S2被按下 case 0: { GPFDAT |= (0x7<<4); // 所有LED熄灭 GPFDAT &= ~(1<<4); // LED1点亮 break; } // S3被按下 case 2: { GPFDAT |= (0x7<<4); // 所有LED熄灭 GPFDAT &= ~(1<<5); // LED2点亮 break; } // K4被按下 case 5: { GPFDAT |= (0x7<<4); // 所有LED熄灭 GPFDAT &= ~(1<<6); // LED4点亮 break; } default: break; } //清中断 if( oft == 5 ) EINTPEND = (1<<11); // EINT8_23合用IRQ5 SRCPND = 1<

2 字符驱动设备 - 按键中断查询

2.1 中断处理体系结构

     每一个中断用一个irq_desc结构体表示:其中irq_desc.irq_chip结构用于操作底层硬件,屏蔽、使能、清除中断等;irq_desc.irqaction 结构用来注册中断处理函数


     可见,中断体系结构的初始化即使构造这些数据结构,比如:irq_desc数组项中的handle_irq、chip等成员;用户注册中断时就是构造action链表;用户卸载中断是就是从action链表中除去不需要的项。
struct irq_desc { unsigned int           irq;    //中断号 
irq_flow_handler_t handle_irq;  //系统中断处理的入口函数   struct irq_chip*chip;  // 对应的irq_chip结构,定义了与中断处理有关的函数,详细讲解如下 struct msi_desc*msi_desc; void *handler_data; void *chip_data; struct irqaction*action;/* IRQ action list */ //用户的中断处理函数,调用request_irq时添加   unsigned intstatus;/* IRQ status */

unsigned intdepth;/* nested irq disables */ unsigned intwake_depth;/* nested wake enables */ unsigned intirq_count;/* For detecting broken IRQs */ unsigned intirqs_unhandled; spinlock_tlock; #ifdef CONFIG_SMP cpumask_taffinity; unsigned intcpu; #endif #if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE) cpumask_tpending_mask; #endif #ifdef CONFIG_PROC_FS struct proc_dir_entry*dir; #endif const char*name; } ____cacheline_internodealigned_in_smp;
  struct irq_chip { const char *name; unsigned int (*startup)(unsigned int irq); void (*shutdown)(unsigned int irq); void (*enable)(unsigned int irq); void (*disable)(unsigned int irq); void (*ack)(unsigned int irq); void (*mask)(unsigned int irq); void (*mask_ack)(unsigned int irq); void (*unmask)(unsigned int irq); void (*eoi)(unsigned int irq); void (*end)(unsigned int irq); void (*set_affinity)(unsigned int irq, cpumask_t dest); int (*retrigger)(unsigned int irq); int (*set_type)(unsigned int irq, unsigned int flow_type); int (*set_wake)(unsigned int irq, unsigned int on); /* Currently used only by UML, might disappear one day.*/ #ifdef CONFIG_IRQ_RELEASE_METHOD void (*release)(unsigned int irq, void *dev_id); #endif /* * For compatibility, ->typename is copied into ->name. * Will disappear. */ const char *typename; };


2.2 用户注册中断处理函数的过程、

     用户用request_irq注册中断函数:      request_irq(IRQ_EINT0,  buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);      request_irq函数调用setup_irq函数
     setup_irq()函数完成三个功能:     1,将新建的irqaction结构联入irq_desc[irq]结构的action链表中;     2,设置irq_desc[irq]结构中chip成员的还没有设置的指针,让它们指向一些默认设置;     3,设置中断触发方式;     4,启动中断(中断使能的意思)

    int request_irq(unsigned int irq, irq_handler_t handler,unsigned long irqflags, const char *devname, void *dev_id)
               action->handler = handler;
action->flags = irqflags;
cpus_clear(action->mask);
action->name = devname;
action->next = NULL;
action->dev_id = dev_id;                 retval = setup_irq(irq, action);

2.3 中断处理过程

P404 看书很无聊 但是书上讲的还是清楚。s3c24xx_init_irq()设置了中断的初始化函数,irq_desc.irq_chip 设置了有关寄存器的设置。     asm_do_IRQ是中断的C语言的入口函数 asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
    asm_do_IRQ函数调用desc_handle_irq(irq, desc)函数,desc_handle_irq函数直接调用desc结构中的handle_irq成员函数,他就是irq_desc[irq].handle_irq.
函数调用连接过程: asm_do_IRQ
       desc_handle_irq(irq, desc)               struct irq_desc *desc = irq_desc + irq;                 desc->handle_irq(irq, desc);              //irq=16+5,由s3c24xx_init_irq()知set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8);                                                                          //所以调用s3c_irq_demux_extint8
                                                                        //所以不同中断号的 .handle_irq()是不一样的及__set_irq_handler()不一样                                                                         //级联中断用set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8);                                                                          //IRQ_EINT8t23=S3C2410_IRQ(5);                                                                         //irq=S3C2410_IRQ(5) 有 set_irq_chained_handler(S3C2410_IRQ(5), s3c_irq_demux_extint8);                                                                                        s3c_irq_demux_extint8
                   irq += (IRQ_EINT4 - 4);                         //irq=44
        desc_handle_irq(irq, irq_desc + irq);    //
                                  desc->handle_irq(irq, desc);   //因为irq=44由s3c24xx_init_irq()知set_irq_handler(irqno, handle_edge_irq);                                                                                    //所以调用handle_edge_irq                                   handle_edge_irq
      desc->chip->ack(irq);  //在init_IRQ中被设置为s3c_irqext_chip
      action_ret = handle_IRQ_event(irq, action);  //该函数来逐个执行action链表中用户注册的中断处理函数;
        do {
      ret = action->handler(irq, action->dev_id);  //执行用户注册的中断处理函数
      if (ret == IRQ_HANDLED)
           status |= action->flags;
       retval |= ret;
               action = action->next;           //下一个
            } while (action);

在工程中搜索__set_irq_handler函数,发现中断处理函数是被定义在 arch/arm/plat-s3c24xx/irq.c/s3c24xx_init_irq()中:
。。。         /* setup the cascade irq handlers */ //设置各个irq的ird_desc[irq].handle_irq()函数如:ird_desc[irq].handle_irq()=s3c_irq_demux_extint8();
set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint4t7);
set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8);
set_irq_chained_handler(IRQ_UART0, s3c_irq_demux_uart0);
set_irq_chained_handler(IRQ_UART1, s3c_irq_demux_uart1);
set_irq_chained_handler(IRQ_UART2, s3c_irq_demux_uart2);
set_irq_chained_handler(IRQ_ADCPARENT, s3c_irq_demux_adc);
/* external interrupts */


for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {
irqdbf("registering irq %d (ext int) ", irqno);
set_irq_chip(irqno, &s3c_irq_eint0t4);
set_irq_handler(irqno, handle_edge_irq);
set_irq_flags(irqno, IRQF_VALID);
}
for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) {
irqdbf("registering irq %d (extended s3c irq) ", irqno);
set_irq_chip(irqno, &s3c_irqext_chip);
set_irq_handler(irqno, handle_edge_irq);
set_irq_flags(irqno, IRQF_VALID);
}

注:从以上代码中可以看出,注册中断主要是注册中断服务程序入口。Linux中将所有的中断号用一个stuctirq_desc数据结构进行统一管理,每个中断号或者一组中断号(如级联的中断),对应一个structirq_desc, 所以根据相应的中断号,可以获取对应的中断描述结构irq_desc: 。。。     所以s3c24xx_init_irq函数用来构造irq_desc结构的各项。
例如:s3c_irq_demux_extint8()函数:



       IRQ_ENINT8--IRQ_EINT23这几个中断的处理函数入口,在init_IRQ函数初始化中断体系结构的时候已经被设置为handle_edge_ipq函数。上面代码的 第575行的代码就是调用这个函数(?)。 handle_edge_irq()
      desc->chip->ack(irq);  //在init_IRQ中被设置为s3c_irqext_chip       action_ret = handle_IRQ_event(irq, action);  //该函数来逐个执行action链表中用户注册的中断处理函数;
        do {       ret = action->handler(irq, action->dev_id);  //执行用户注册的中断处理函数       if (ret == IRQ_HANDLED)            status |= action->flags;        retval |= ret;                action = action->next;           //下一个             } while (action);
和裸板相似的寄存器设置在struct irq_chip*chip; 中
附录:s3c24xx_init_irq源码: void __init s3c24xx_init_irq(void) { unsigned long pend; unsigned long last; int irqno; int i; irqdbf("s3c2410_init_irq: clearing interrupt status flags "); /* first, clear all interrupts pending... */ last = 0; for (i = 0; i < 4; i++) { pend = __raw_readl(S3C24XX_EINTPEND); if (pend == 0 || pend == last) break; __raw_writel(pend, S3C24XX_EINTPEND); printk("irq: clearing pending ext status %08x ", (int)pend); last = pend; } last = 0; for (i = 0; i < 4; i++) { pend = __raw_readl(S3C2410_INTPND); if (pend == 0 || pend == last) break; __raw_writel(pend, S3C2410_SRCPND); __raw_writel(pend, S3C2410_INTPND); printk("irq: clearing pending status %08x ", (int)pend); last = pend; } last = 0; for (i = 0; i < 4; i++) { pend = __raw_readl(S3C2410_SUBSRCPND); if (pend == 0 || pend == last) break; printk("irq: clearing subpending status %08x ", (int)pend); __raw_writel(pend, S3C2410_SUBSRCPND); last = pend; } /* register the main interrupts */ irqdbf("s3c2410_init_irq: registering s3c2410 interrupt handlers "); for (irqno = IRQ_EINT4t7; irqno <= IRQ_ADCPARENT; irqno++) { /* set all the s3c2410 internal irqs */ switch (irqno) { /* deal with the special IRQs (cascaded) */ /* 这几个中断是级联的,所以中断例程也不一样.*/ case IRQ_EINT4t7: case IRQ_EINT8t23: case IRQ_UART0: case IRQ_UART1: case IRQ_UART2: case IRQ_ADCPARENT: set_irq_chip(irqno, &s3c_irq_level_chip); set_irq_handler(irqno, handle_level_irq); /*这个中断例程在后面会被重新设置*/ break; case IRQ_RESERVED6: case IRQ_RESERVED24: /* no IRQ here */ break; default: //irqdbf("registering irq %d (s3c irq) ", irqno); set_irq_chip(irqno, &s3c_irq_chip); set_irq_handler(irqno, handle_edge_irq); /*对于不是级联的中断,这个就是中断例程.*/ set_irq_flags(irqno, IRQF_VALID); } } /* 重新设置级联的中断例程*/ /* setup the cascade irq handlers */ set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint4t7); set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8); set_irq_chained_handler(IRQ_UART0, s3c_irq_demux_uart0); set_irq_chained_handler(IRQ_UART1, s3c_irq_demux_uart1); set_irq_chained_handler(IRQ_UART2, s3c_irq_demux_uart2); set_irq_chained_handler(IRQ_ADCPARENT, s3c_irq_demux_adc); /* external interrupts */ /* 为外部中断设置中断例程*/ for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) { irqdbf("registering irq %d (ext int) ", irqno); set_irq_chip(irqno, &s3c_irq_eint0t4); set_irq_handler(irqno, handle_edge_irq); set_irq_flags(irqno, IRQF_VALID); } for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) { irqdbf("registering irq %d (extended s3c irq) ", irqno); set_irq_chip(irqno, &s3c_irqext_chip); set_irq_handler(irqno, handle_edge_irq); set_irq_flags(irqno, IRQF_VALID); } /* register the uart interrupts */ /* 为UART设置中断例程,*/ irqdbf("s3c2410: registering external interrupts "); for (irqno = IRQ_S3CUART_RX0; irqno <= IRQ_S3CUART_ERR0; irqno++) { irqdbf("registering irq %d (s3c uart0 irq) ", irqno); set_irq_chip(irqno, &s3c_irq_uart0); set_irq_handler(irqno, handle_level_irq); set_irq_flags(irqno, IRQF_VALID); } for (irqno = IRQ_S3CUART_RX1; irqno <= IRQ_S3CUART_ERR1; irqno++) { irqdbf("registering irq %d (s3c uart1 irq) ", irqno); set_irq_chip(irqno, &s3c_irq_uart1); set_irq_handler(irqno, handle_level_irq); set_irq_flags(irqno, IRQF_VALID); } for (irqno = IRQ_S3CUART_RX2; irqno <= IRQ_S3CUART_ERR2; irqno++) { irqdbf("registering irq %d (s3c uart2 irq) ", irqno); set_irq_chip(irqno, &s3c_irq_uart2); set_irq_handler(irqno, handle_level_irq); set_irq_flags(irqno, IRQF_VALID); } for (irqno = IRQ_TC; irqno <= IRQ_ADC; irqno++) { irqdbf("registering irq %d (s3c adc irq) ", irqno); set_irq_chip(irqno, &s3c_irq_adc); set_irq_handler(irqno, handle_edge_irq); set_irq_flags(irqno, IRQF_VALID); } irqdbf("s3c2410: registered interrupt handlers "); }

3 字符驱动按键中断查询源码

// third_drv.c #include #include #include #include #include #include #include #include #include #include #include static struct class *thirddrv_class; static struct class_device *thirddrv_class_dev; volatile unsigned long *gpfcon; volatile unsigned long *gpfdat; volatile unsigned long *gpgcon; volatile unsigned long *gpgdat; static DECLARE_WAIT_QUEUE_HEAD(button_waitq); //生成一个等待队列头wait_queue_head_t,名字为button_waitq /* 中断事件标志, 中断服务程序将它置1,third_drv_read将它清0 */ static volatile int ev_press = 0; //作为设备id 及 dev_id,作为request_irq函数 的参数void *dev_id struct pin_desc{ unsigned int pin; unsigned int key_val; }; /* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */ /* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */ static unsigned char key_val; struct pin_desc pins_desc[4] = { {S3C2410_GPF0, 0x01}, {S3C2410_GPF2, 0x02}, {S3C2410_GPG3, 0x03}, {S3C2410_GPG11, 0x04}, }; /* * 确定按键值 */ //中断处理函数第一个被触发的中断的ID,第二个被触发的设备id及dev_id, //dev_id 在request_irq中输入 static irqreturn_t buttons_irq(int irq, void *dev_id) { struct pin_desc * pindesc = (struct pin_desc *)dev_id; unsigned int pinval; pinval = s3c2410_gpio_getpin(pindesc->pin); /* s3c2410_gpio_getpin()的返回值是GPxDAT寄存器的值与所要读取的 GPIO对应的bit mask相与以后的值,0表示该GPIO对应的bit为0, 非0表示该bit为1,所以s3c2410_gpio_getpin(S3C2410_GPG(9))如果GPG9为 低电平则返回的是0,如果是高电平则返回的是GPxDAT中的 GPG9对应位的值为0x0100而不是0x0001,查处问题后修改也很 简单了。 */ if (pinval) { /* 松开 */ key_val = 0x80 | pindesc->key_val; } else { /* 按下 */ key_val = pindesc->key_val; } ev_press = 1; /* 表示中断发生了 */ wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */ return IRQ_RETVAL(IRQ_HANDLED); } static int third_drv_open(struct inode *inode, struct file *file) { /* 配置GPF0,2为输入引脚 */ /* 配置GPG3,11为输入引脚 */ //int request_irq(unsigned int irq, irq_handler_t handler,unsigned long irqflags, const char *devname, void *dev_id) // 中断号,中断入口函数,中断触发方式,申请的中断名用cat /proc/interrupts命令查看,自定义的设备id //dev_id 随意给可以是1 或0 等,韦东山给了结构体 request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]); request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]); request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]); request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]); return 0; } ssize_t third_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) { if (size != 1) return -EINVAL; /* 如果没有按键动作,ev_press=0 休眠 */ /*休眠 将进程放入button_waitq等待队列中*/ /*唤醒休眠ake_up_interruptible(&button_waitq); */ wait_event_interruptible(button_waitq, ev_press); /* 如果有按键动作, 返回键值 */ copy_to_user(buf, &key_val, 1); ev_press = 0; return 1; } int third_drv_close(struct inode *inode, struct file *file) { //注销申请的中断,执行后调用cat /proc/interrupts命令将看不到中断名 free_irq(IRQ_EINT0, &pins_desc[0]); free_irq(IRQ_EINT2, &pins_desc[1]); free_irq(IRQ_EINT11, &pins_desc[2]); free_irq(IRQ_EINT19, &pins_desc[3]); return 0; } static struct file_operations sencod_drv_fops = { .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ .open = third_drv_open, .read = third_drv_read, .release = third_drv_close, }; int major; static int third_drv_init(void) { major = register_chrdev(0, "third_drv", &sencod_drv_fops); thirddrv_class = class_create(THIS_MODULE, "third_drv"); thirddrv_class_dev = class_device_create(thirddrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */ gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16); gpfdat = gpfcon + 1; gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16); gpgdat = gpgcon + 1; return 0; } static void third_drv_exit(void) { unregister_chrdev(major, "third_drv"); class_device_unregister(thirddrv_class_dev); class_destroy(thirddrv_class); iounmap(gpfcon); iounmap(gpgcon); return 0; } module_init(third_drv_init); module_exit(third_drv_exit); MODULE_LICENSE("GPL");

//thirddrvtest.c #include #include #include #include /* thirddrvtest */ int main(int argc, char **argv) { int fd; unsigned char key_val; fd = open("/dev/buttons", O_RDWR); if (fd < 0) { printf("can't open! "); } while (1) { read(fd, &key_val, 1); printf("key_val = 0x%x ", key_val); } return 0; }



lsmod:查看驱动 打开中断就会注册中断: cat /proc/interrupts 查看中断 打开设备 exec 5 ps; kill -9 787 lsmod rmmod third_drv ./thirddrvtest & 在后台运行程序 ps //查看 在slip状态 cat /proc/interrupts top //查看占用cpu资源




lsmod:查看驱动 打开中断就会注册中断: cat /proc/interrupts 查看中断 打开设备 exec 5 ps; kill -9 787 lsmod rmmod third_drv ./thirddrvtest & 在后台运行程序 ps //查看 在slip状态 cat /proc/interrupts top //查看占用cpu资源