中断处理体系结构

来源:互联网 发布:阿里云服务器机房地址 编辑:程序博客网 时间:2024/05/22 15:08

在裸板程序按键中断工程int下interrupt.c包含中断处理函数EINT_Handle( )的实现:

#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<<oft;    INTPND = 1<<oft;}
由此程序可知,分3步

⑴读取寄存器INTOFFSET,分辨是哪个中断

⑵对不同的中断调用中断服务函数或者相应的处理

⑶清中断

其中步骤⑶可以再⑴之前

同样,在内核中中断程序处理流程也类似,下面分析中断处理函数asm_do_IRQ:

Linux内核将所有中断统一编号,使用irq_desc结构体数组来描述这些中断,每个数组项对应一个中断或一组中断(共用一个中断号),位于include/linux/irq.h

struct irq_desc {irq_flow_handler_thandle_irq;struct irq_chip*chip;struct msi_desc*msi_desc;void*handler_data;void*chip_data;struct irqaction*action;/* IRQ action list */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;struct proc_dir_entry*dir;const char*name;} ____cacheline_internodealigned_in_smp;


其中handle_irq为这个或这组中断的处理函数入口。中断发生是,总入口函数asm_do_IRQ根据中断号调用对应的irq_desc数组项中的handle_irq.

asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs){struct irq_desc *desc = irq_desc + irq;desc_handle_irq(irq, desc);}

其中irq是用户注册相应中断处理函数传入的实参,

request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char * devname, void * dev_id)

irq_desc为irq_desc数组项irq_desc[0]的地址,irq_desc为全局变量,其定义并初始化在kernel/irq/handle.c中,所以irq_desc[0]的地址是已知的即irq_desc结构体分配了内存空间,其数组首地址是确定的,对应相应的中断desc指针值是已知的,所以desc_handle_irq的参数irq, desc均是已知的:

struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {[0 ... NR_IRQS-1] = {.status = IRQ_DISABLED,.chip = &no_irq_chip,.handle_irq = handle_bad_irq,.depth = 1,.lock = __SPIN_LOCK_UNLOCKED(irq_desc->lock),#ifdef CONFIG_SMP.affinity = CPU_MASK_ALL#endif}};

static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc){desc->handle_irq(irq, desc);}

然而中断号为irq这个中断或这组中断入口函数handle_irq还没有初始化(即将另一函数地址赋值给handle_irq)

void  __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,const char *name){struct irq_desc *desc;unsigned long flags;desc->handle_irq = handle;desc->name = name;}

handle_irq=handle,handle是__set_irq_handler的形参,  handle函数是由linux/irq.h中的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);}
对应外部中断EINT4来说:

void __init s3c24xx_init_irq(void){unsigned long pend;unsigned long last;int irqno;int i;/* external interrupts */for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) {irqdbf("registering irq %d (extended s3c irq)\n", irqno);set_irq_chip(irqno, &s3c_irqext_chip);set_irq_handler(irqno, handle_edge_irq);set_irq_flags(irqno, IRQF_VALID);}}

irq_desc[irq]数组项的成员chip, handle_irq由set_irq_chip和set_irq_handler初始化即:

desc->handle_irq = handle_edge_irq

desc->chip = s3c_irqext_chip

继续分析handle_edge_irq函数和s3c_irqext_chip的实现:

<span style="font-family: Arial, Helvetica, sans-serif;"></span>
/** *handle_edge_irq - edge type IRQ handler *@irq:the interrupt number *@desc:the interrupt description structure for this irq * *Interrupt occures on the falling and/or rising edge of a hardware *signal. The occurence is latched into the irq controller hardware *and must be acked in order to be reenabled. After the ack another *interrupt can happen on the same source even before the first one *is handled by the assosiacted event handler. If this happens it *might be necessary to disable (mask) the interrupt depending on the *controller hardware. This requires to reenable the interrupt inside *of the loop which handles the interrupts which have arrived while *the handler was running. If all pending interrupts are handled, the *loop is left. */void fastcallhandle_edge_irq(unsigned int irq, struct irq_desc *desc){const unsigned int cpu = smp_processor_id();spin_lock(&desc->lock);desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);/* * If we're currently running this IRQ, or its disabled, * we shouldn't process the IRQ. Mark it pending, handle * the necessary masking and go out */if (unlikely((desc->status & (IRQ_INPROGRESS | IRQ_DISABLED)) ||    !desc->action)) {desc->status |= (IRQ_PENDING | IRQ_MASKED);mask_ack_irq(desc, irq);goto out_unlock;}kstat_cpu(cpu).irqs[irq]++;/* Start handling the irq */desc->chip->ack(irq);   /*xyc:清中断*//* Mark the IRQ currently in progress.*/desc->status |= IRQ_INPROGRESS;do {struct irqaction *action = desc->action;irqreturn_t action_ret;if (unlikely(!action)) {desc->chip->mask(irq);goto out_unlock;}/* * When another irq arrived while we were handling * one, we could have masked the irq. * Renable it, if it was not disabled in meantime. */if (unlikely((desc->status &       (IRQ_PENDING | IRQ_MASKED | IRQ_DISABLED)) ==      (IRQ_PENDING | IRQ_MASKED))) {desc->chip->unmask(irq);desc->status &= ~IRQ_MASKED;}desc->status &= ~IRQ_PENDING;spin_unlock(&desc->lock);action_ret = handle_IRQ_event(irq, action);  /*handle_IRQ_event:处理中断*/if (!noirqdebug)note_interrupt(irq, desc, action_ret);spin_lock(&desc->lock);} while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING);desc->status &= ~IRQ_INPROGRESS;out_unlock:spin_unlock(&desc->lock);}


包括:

①desc->chip->ack(irq);   /*xyc:清中断*/

②action_ret = handle_IRQ_event(irq, action);  /*handle_IRQ_event:处理中断*/

继续分析handle_IRQ_event函数:


</pre><pre name="code" class="cpp">/** * handle_IRQ_event - irq action chain handler * @irq:the interrupt number * @action:the interrupt action chain for this irq * * Handles the action chain of an irq event */irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action){irqreturn_t ret, retval = IRQ_NONE;unsigned int status = 0;handle_dynamic_tick(action);if (!(action->flags & IRQF_DISABLED))local_irq_enable_in_hardirq();do {request_irqret = action->handler(irq, action->dev_id);  /*调用action中的成语handler处理中断*/if (ret == IRQ_HANDLED)status |= action->flags;retval |= ret;action = action->next;} while (action);if (status & IRQF_SAMPLE_RANDOM)add_interrupt_randomness(irq);local_irq_disable();return retval;}
</pre><p>handle_IRQ_event调用action->handler,即desc->action->handler,而action->handler还没初始化,用户需要编写中断处理函数handler,action结构的声明如下:</p><p></p><pre code_snippet_id="376473" snippet_file_name="blog_20140604_13_2229378" name="code" class="cpp"><pre name="code" class="cpp">struct irqaction {irq_handler_t handler;/*需要用户编写中断服务函数*/unsigned long flags;/*用户确定触发条件,比如低电平,双边沿*/cpumask_t mask;const char *name;/*cat /proc/interrupts显示的中断名称*/void *dev_id;/*对使用同一中断号的一组中断,action出链时,free_irq(irq, dev_id)需要dev_id判断是哪个action出链*/struct irqaction *next;int irq;/*内核统一编码的中断号,需要用户看原理图确定*/struct proc_dir_entry *dir;};

现在需要用户调用request_irq分配action结构体,然后用request_irq的实参填充action结构体,request_irq的实现内核做好了,所以用户需要确定request_irq的实参,然后编写驱动时调用request_irq函数:

int request_irq(unsigned int irq, irq_handler_t handler,unsigned long irqflags, const char *devname, void *dev_id){struct irqaction *action;int retval;action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);if (!action)return -ENOMEM;action->handler = handler;action->flags = irqflags;cpus_clear(action->mask);action->name = devname;action->next = NULL;action->dev_id = dev_id;select_smp_affinity(irq);retval = setup_irq(irq, action);if (retval)kfree(action);return retval;}
首先①kmalloc分配action结构体,②然后用request_irq的参数填充action,现在这个新分配的action还没有挂载到desc->action数组项的irqaction链表中:

action->handler = handler;action->flags = irqflags;cpus_clear(action->mask);action->name = devname;action->next = NULL;action->dev_id = dev_id;

action结构注册到desc->action中,由request_irq调用setup_irq实现:


/* * Internal function to register an irqaction - typically used to * allocate special interrupts that are part of the architecture. */int setup_irq(unsigned int irq, struct irqaction *new){struct irq_desc *desc = irq_desc + irq;struct irqaction *old, **p;const char *old_name = NULL;unsigned long flags;int shared = 0;if (irq >= NR_IRQS)return -EINVAL;if (desc->chip == &no_irq_chip)return -ENOSYS;/* * Some drivers like serial.c use request_irq() heavily, * so we have to be careful not to interfere with a * running system. */if (new->flags & IRQF_SAMPLE_RANDOM) {/* * This function might sleep, we want to call it first, * outside of the atomic block. * Yes, this might clear the entropy pool if the wrong * driver is attempted to be loaded, without actually * installing a new handler, but is this really a problem, * only the sysadmin is able to do this. */rand_initialize_irq(irq);}/* * The following block of code has to be executed atomically */spin_lock_irqsave(&desc->lock, flags);p = &desc->action;old = *p;if (old) {/* * Can't share interrupts unless both agree to and are * the same type (level, edge, polarity). So both flag * fields must have IRQF_SHARED set and the bits which * set the trigger type must match. */ /* action不为空,但是不允许共享中断,就mismatch*/if (!((old->flags & new->flags) & IRQF_SHARED) ||    ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK)) {old_name = old->name;goto mismatch;}#if defined(CONFIG_IRQ_PER_CPU)/* All handlers must agree on per-cpuness */if ((old->flags & IRQF_PERCPU) !=    (new->flags & IRQF_PERCPU))goto mismatch;#endif/*不为空,且允许共享中断号,shared由0变为1,经过do{}while,此时p等于最后一个irqaction结构成员next的地址*//* add new interrupt at end of irq queue */do {p = &old->next;old = *p;} while (old);shared = 1;}*p = new;/*将request_irq分配的action加入到irqaction链表中*//* Exclude IRQ from balancing */if (new->flags & IRQF_NOBALANCING)desc->status |= IRQ_NO_BALANCING;/*shared为1表示 action链表中有2个 action项了,包括刚加的一个*//*shared为0表示链表中只要刚刚加入的new irqaction结构,这时需要设置默认的使能屏蔽等函数*/if (!shared) {irq_chip_set_defaults(desc->chip);#if defined(CONFIG_IRQ_PER_CPU)if (new->flags & IRQF_PERCPU)desc->status |= IRQ_PER_CPU;#endif/* Setup the type (level, edge polarity) if configured: */if (new->flags & IRQF_TRIGGER_MASK) {if (desc->chip && desc->chip->set_type)desc->chip->set_type(irq,  new->flags & IRQF_TRIGGER_MASK);  /*设置为中断引脚,触发方式为水平,单边沿,双边沿等等*/else/* * IRQF_TRIGGER_* but the PIC does not support * multiple flow-types? */printk(KERN_WARNING "No IRQF_TRIGGER set_type "       "function for IRQ %d (%s)\n", irq,       desc->chip ? desc->chip->name :       "unknown");} elsecompat_irq_chip_set_default_handler(desc);desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING |  IRQ_INPROGRESS);if (!(desc->status & IRQ_NOAUTOEN)) {desc->depth = 0;desc->status &= ~IRQ_DISABLED;if (desc->chip->startup)desc->chip->startup(irq);/*使能中断*/elsedesc->chip->enable(irq);} else/* Undo nested disables: */desc->depth = 1;}/* Reset broken irq detection when installing new handler */desc->irq_count = 0;desc->irqs_unhandled = 0;spin_unlock_irqrestore(&desc->lock, flags);new->irq = irq;register_irq_proc(irq);new->dir = NULL;register_handler_proc(irq, new);return 0;mismatch:#ifdef CONFIG_DEBUG_SHIRQif (!(new->flags & IRQF_PROBE_SHARED)) {printk(KERN_ERR "IRQ handler type mismatch for IRQ %d\n", irq);if (old_name)printk(KERN_ERR "current handler: %s\n", old_name);dump_stack();}#endifspin_unlock_irqrestore(&desc->lock, flags);return -EBUSY;}

set_irq会找到desc->action挂载的链表irqaction的链表尾,然后将request_irq分配的action挂载在链表尾,其分为两种情况:

①如果先前有挂载irqaction,这次action如果不允许共享此中断号,则挂载失败

②如果可以共享此中断号,p等于链表尾成员next的地址,且shared由0变为1,

最后通过*p =new将action挂载到链表中,然后根据shared的值可以判断new是第一个链表项,还是2和2以后的链表项,若是第一个链表项(此时shared = 0),则需要设置此中断其对应引脚为中断引脚,触发方式,最后使能中断,若是还是2和2以后的链表项,则先前第一个链表项已经设置了这3项功能(中断引脚,触发方式,使能中断)


到了这里,若对于EINT4来说,内核帮我们初始化了handle_irq, chip, :


struct irq_desc {irq_flow_handler_thandle_irq;     /*内核初始化handle_irq = handle_edge_irq*/struct irq_chip*chip;/*内核初始化为chip = s3c_irq_eint0t4*/struct msi_desc*msi_desc;void*handler_data;void*chip_data;struct irqaction*action;/* 内核用request_irq构造action链表,action链表项中的成员值由用户确定 */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;struct proc_dir_entry*dir;const char*name;} ____cacheline_internodealigned_in_smp;

内核初始化handle_irq后,发生中断就会调用handle_edge_irq,其完成①清中断,②调用action->handler,而用户调用request_irq时用其实参构造并初始化了action结构体

<pre name="code" class="cpp">struct irqaction {irq_handler_t handler;  /*需要用户编写中断服务函数*/unsigned long flags;/*用户确定触发条件,比如低电平,双边沿*/cpumask_t mask;const char *name;/*cat /proc/interrupts显示的中断名称*/void *dev_id;/*对使用同一中断号的一组中断,action出链时,free_irq(irq, dev_id)需要dev_id判断是哪个action出链*/struct irqaction *next;int irq;/*内核统一编码的中断号,需要用户看原理图和asm-arm/s3c2410/irqs.h确定*/struct proc_dir_entry *dir;};

整个中断处理函数框架为下图:

由上述可知,需要确定request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char * devname, void * dev_id)各个参数项,对应action结构体主要成员,然后调用request_irq,比如对于EINT4来说可以这样调用

request_irq(IRQ_EINT4, buttons_irq, IRQT_BOTHEDGE, "s4", 1);

buttons_irq函数需要用户编写:

编写的通过中断获取按键值的驱动源码third_drv.c如下:


/****************************************************************filename: third_drv.c*description:通过中断的方法获取按键值*author:    xyc*create time:2014/6/5*version:  1*modify info:****************************************************************/#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/irqreturn.h>#include <linux/irq.h>#include <linux/wait.h>#include <asm/irq.h>#include <asm/arch/regs-gpio.h>#include <asm/hardware.h>#include <asm-arm/io.h>#include <asm-arm/uaccess.h>static struct class *thirddrv_class;static struct class_device*thirddrv_class_dev;static unsigned char key_val;static DECLARE_WAIT_QUEUE_HEAD(buttons_waitq); struct pin_desc {unsigned int pin;unsigned int key_val;};/* *按下:0x01, 0x02 *松开:0x81, 0x82 */static struct pin_desc pins_desc[2] = {{S3C2410_GPF0,0x01},{S3C2410_GPF2,0x02},};static volatile unsigned int condition= 0;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);/*确定键值*/if(pinval){/*松开*/key_val = 0x80 | pindesc->key_val ;//printk("kernel key_val = 0x%x\n", key_val);}else{/*按下*/key_val = pindesc->key_val ;//printk("kernel key_val = 0x%x\n", key_val);}/*按键按下或松开中断发生将condition = 1使得满足wait_event_interruptible唤醒的条件*/condition = 1;/*唤醒休眠的进程./third_drv_test */wake_up_interruptible(&buttons_waitq);return IRQ_HANDLED;}static int third_drv_open(struct inode *inode, struct file *file){request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "s2", &pins_desc[0]);request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "s3", &pins_desc[1]);return 0;}static ssize_t third_drv_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos){if(nbytes != 1)return EINVAL;/*没有按键按下时,./third_drv_test 进程休眠,*/wait_event_interruptible(buttons_waitq, condition);/*有按键按下或松开时,结束休眠,并将键值传给应用程序*/copy_to_user(buf, &key_val, 1);/*当下一次应用调用read时,condition = 0使得./third_drv_test 进程休眠*/condition = 0;return 1;}static int third_drv_release(struct inode * inode, struct file * file){free_irq(IRQ_EINT0, &pins_desc[0]);free_irq(IRQ_EINT2, &pins_desc[1]);return 0;}static const struct file_operations third_drv_fops = {.owner= THIS_MODULE,.read= third_drv_read,.open= third_drv_open,.release=third_drv_release,};int major;static int __init third_drv_init(void){major = register_chrdev(0, "third_drv", &third_drv_fops);thirddrv_class = class_create(THIS_MODULE, "third_drv");    thirddrv_class_dev = class_device_create(thirddrv_class, NULL, MKDEV(major, 0), NULL, "buttons");return 0;}static void __exit third_drv_exit(void){    /* 卸载驱动程序 */    unregister_chrdev(major, "third_drv");    class_device_unregister(thirddrv_class_dev);    class_destroy(thirddrv_class);}/* 这两行指定驱动程序的初始化函数和卸载函数 */module_init(third_drv_init);module_exit(third_drv_exit);MODULE_LICENSE("GPL");

third_drv.c编译成third_drv.ko的Makefile为

KERN_DIR = /work/system/linux-2.6.22.6all:        make -C $(KERN_DIR) M=`pwd` modules clean:        make -C $(KERN_DIR) M=`pwd` modules clean        rm -rf modules.orderobj-m   += third_drv.o

测试代码third_drv_test.c代码为:


#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>int main(int argc, char *argv[]){int fd1;unsigned char keys_val;fd1 = open("/dev/buttons", O_RDWR);if(fd1<0)printf("open failed\n");while(1){read(fd1, &keys_val, 1);printf("keys_val = 0x%x\n", keys_val);}return 0;}

测试:

可以单独测试驱动源码third_drv.c中的third_drv_open和buttons_irq函数,当然buttons_irq中的2行相同打印应该打开:

printk("kernel key_val = 0x%x\n", key_val);
当然应该先lsmod看是否有旧的third_drv.ko模块,若有,则 rmmod  third_drv.ko

# insmod third_drv.ko# exec 5</dev/buttons # cat proc/interrupts            CPU0 16:          8    s3c-ext0  s2 18:          4    s3c-ext0  s3 30:      58489         s3c  S3C2410 Timer Tick 32:          0         s3c  s3c2410-lcd 33:          0         s3c  s3c-mci 34:          0         s3c  I2SSDI 35:          0         s3c  I2SSDO 37:         12         s3c  s3c-mci 42:          0         s3c  ohci_hcd:usb1 43:          0         s3c  s3c2440-i2c 51:       1416     s3c-ext  eth0 60:          0     s3c-ext  s3c-mci 70:        319   s3c-uart0  s3c2440-uart 71:        369   s3c-uart0  s3c2440-uart 79:          0     s3c-adc  s3c2410_action 80:          0     s3c-adc  s3c2410_action 83:          0           -  s3c2410-wdtErr:          0# ps      PID  Uid        VSZ Stat Command    1 0          3092 S   init         2 0               SW< [kthreadd]    3 0               SWN [ksoftirqd/0]    4 0               SW< [watchdog/0]    5 0               SW< [events/0]    6 0               SW< [khelper]   55 0               SW< [kblockd/0]   56 0               SW< [ksuspend_usbd]   59 0               SW< [khubd]   61 0               SW< [kseriod]   73 0               SW  [pdflush]   74 0               SW  [pdflush]   75 0               SW< [kswapd0]   76 0               SW< [aio/0]  710 0               SW< [mtdblockd]  745 0               SW< [kmmcd]  762 0               SW< [rpciod/0]  770 0          3096 S   -sh   788 0          3096 R   ps # ls -l proc/770/fdproc/770/fd/      proc/770/fdinfo/# ls -l proc/770/fd/*ls: proc/770/fd/3: No such file or directorylrwx------    1 0        0              64 Jan  1 00:03 proc/770/fd/0 -> /dev/consolelrwx------    1 0        0              64 Jan  1 00:03 proc/770/fd/1 -> /dev/consolelrwx------    1 0        0              64 Jan  1 00:03 proc/770/fd/10 -> /dev/ttylrwx------    1 0        0              64 Jan  1 00:03 proc/770/fd/2 -> /dev/consolelr-x------    1 0        0              64 Jan  1 00:03 proc/770/fd/5 -> /dev/buttons

打开/dev/buttons设备用exec命令,exec 5</dev/buttons表示将设备/dev/buttons打开并重定位到文件描述符5,即操作文件描述符5就是操作/dev/buttons

关闭/dev/buttons用exec 5<&-  这时文件描述符5和/dev/buttons脱钩了(用rmmod之前必须 exec 5<&- 用来关闭dev/buttons)

快速按键和慢速按键:

# kernel key_val = 0x1kernel key_val = 0x81kernel key_val = 0x1kernel key_val = 0x1kernel key_val = 0x81kernel key_val = 0x1kernel key_val = 0x1kernel key_val = 0x81kernel key_val = 0x1kernel key_val = 0x81kernel key_val = 0x1kernel key_val = 0x1kernel key_val = 0x81kernel key_val = 0x81kernel key_val = 0x1kernel key_val = 0x81kernel key_val = 0x1kernel key_val = 0x81

存在问题:可知因按键是机械的,存在抖动,所以存在将按下或松开动作当作了发生了多个动作

用测试代码测试,将刚刚的2行驱动打印屏蔽掉,然后编译并复制third_drv.ko到网络根文件系统下:

#exec 5<&-   

#rmmod third_drv

# insmod third_drv.ko# ./third_drv_test &# keys_val = 0x2keys_val = 0x82keys_val = 0x2keys_val = 0x2keys_val = 0x82keys_val = 0x2keys_val = 0x82keys_val = 0x2keys_val = 0x82# topMem: 6724K used, 54460K free, 0K shrd, 0K buff, 2100K cachedCPU:   0% usr   0% sys   0% nice  99% idle   0% io   0% irq   0% softirqLoad average: 0.00 0.00 0.00  PID  PPID USER     STAT   VSZ %MEM %CPU COMMAND  801   770 0        R     3092   5%   1% top   770     1 0        S     3096   5%   0% -sh     1     0 0        S     3092   5%   0% init       800   770 0        S     1312   2%   0% ./third_drv_test     6     2 0        SW<      0   0%   0% [khelper]  762     2 0        SW<      0   0%   0% [rpciod/0]  745     2 0        SW<      0   0%   0% [kmmcd]    2     0 0        SW<      0   0%   0% [kthreadd]    3     2 0        SWN      0   0%   0% [ksoftirqd/0]    4     2 0        SW<      0   0%   0% [watchdog/0]    5     2 0        SW<      0   0%   0% [events/0]   55     2 0        SW<      0   0%   0% [kblockd/0]   56     2 0        SW<      0   0%   0% [ksuspend_usbd]   59     2 0        SW<      0   0%   0% [khubd]   61     2 0        SW<      0   0%   0% [kseriod]   73     2 0        SW       0   0%   0% [pdflush]   74     2 0        SW       0   0%   0% [pdflush]   75     2 0        SW<      0   0%   0% [kswapd0]   76     2 0        SW<      0   0%   0% [aio/0]  710     2 0        SW<      0   0%   0% [mtdblockd]

由top可知按键使用中断上报键值和休眠后,./third_drv_test进程所占CPU接近%0,但是却存在2个问题

①按键抖动--可用定时器去抖动

②如果没有按键,测试代码的read进程./third_drv_test一直休眠,不会返回,如果我想5s没有按键中断发生同样返回(在5s内发生的中断当然立刻返回键值),就需要用到poll机制,


NOTE:关于应用read返回值,和third_drv_read的return 语句的返回值相同,如果将return 1;改为,return 4;测试代码中打印出read的返回值则为4,添加的代码由注释说明,测试代码为:

#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>int main(int argc, char *argv[]){/*用于测试read的返回值和驱动函数third_drv_read返回值是否相同*/int fd1, ret;unsigned char keys_val;fd1 = open("/dev/buttons", O_RDWR);if(fd1<0)printf("open failed\n");while(1){ret = read(fd1, &keys_val, 1);  /*增加取出返回值*/printf("ret =0x%x\n", ret);    /*打印返回值*/printf("keys_val = 0x%x\n", keys_val);}return 0;}


卸载模块卸载不了的问题:
# rmmod third_drv
rmmod: third_drv: Resource temporarily unavailable

是因为这个驱动模块还有用户使用,可以用lsmod 查看third_drv模块使用的用户数为2,比如./third_drv_test测试进程调用了third_drv_read,而后执行完wait_event_interruptible(buttons_waitq, condition);这行时,进程休眠了

# lsmod
Module                  Size  Used by    Not tainted
third_drv               3124  2 

因此需要杀掉这个使用third_drv模块的进程./third_drv_test  ,使用kill -9 pid ,用ps查看./third_drv_test的pid

# ps  PID  Uid        VSZ Stat Command    1 0          3092 S   init         2 0               SW< [kthreadd]    3 0               SWN [ksoftirqd/0]    4 0               SW< [watchdog/0]    5 0               SW< [events/0]    6 0               SW< [khelper]   55 0               SW< [kblockd/0]   56 0               SW< [ksuspend_usbd]   59 0               SW< [khubd]   61 0               SW< [kseriod]   73 0               SW  [pdflush]   74 0               SW  [pdflush]   75 0               SW< [kswapd0]   76 0               SW< [aio/0]  710 0               SW< [mtdblockd]  745 0               SW< [kmmcd]  762 0               SW< [rpciod/0]  770 0          3096 S   -sh   783 0          1312 S   ./third_drv_test 

kill -9 783

这时rmmod third_drv/rmmod third_drv.ko均可卸载(第一个为register_chdev中的第二个参数,模块名,cat /proc/devices可显示)

如果只改变了测试代码,而驱动代码没变,只需要杀掉测试进程./third_drv_test ,而不需要用rmmod third_drv来卸载驱动模块


                                             
0 0