在裸板程序按键中断工程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_t handle_irq;
struct irq_chip *chip;
struct msi_desc *msi_desc;
void *handler_data;
void *chip_data;
struct irqaction *action; /* IRQ action list */
unsigned int status; /* IRQ status */
unsigned int depth; /* nested irq disables */
unsigned int wake_depth; /* nested wake enables */
unsigned int irq_count; /* For detecting broken IRQs */
unsigned int irqs_unhandled;
spinlock_t lock;
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 fastcall
handle_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_irq
ret = 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");
} else
compat_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);/*使能中断*/
else
desc->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_SHIRQ
if (!(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();
}
#endif
spin_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_t handle_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 int status; /* IRQ status */
unsigned int depth; /* nested irq disables */
unsigned int wake_depth; /* nested wake enables */
unsigned int irq_count; /* For detecting broken IRQs */
unsigned int irqs_unhandled;
spinlock_t lock;
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.6
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
obj-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-wdt
Err: 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/fd
proc/770/fd/ proc/770/fdinfo/
# ls -l proc/770/fd/*
ls: proc/770/fd/3: No such file or directory
lrwx------ 1 0 0 64 Jan 1 00:03 proc/770/fd/0 -> /dev/console
lrwx------ 1 0 0 64 Jan 1 00:03 proc/770/fd/1 -> /dev/console
lrwx------ 1 0 0 64 Jan 1 00:03 proc/770/fd/10 -> /dev/tty
lrwx------ 1 0 0 64 Jan 1 00:03 proc/770/fd/2 -> /dev/console
lr-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 = 0x1
kernel key_val = 0x81
kernel key_val = 0x1
kernel key_val = 0x1
kernel key_val = 0x81
kernel key_val = 0x1
kernel key_val = 0x1
kernel key_val = 0x81
kernel key_val = 0x1
kernel key_val = 0x81
kernel key_val = 0x1
kernel key_val = 0x1
kernel key_val = 0x81
kernel key_val = 0x81
kernel key_val = 0x1
kernel key_val = 0x81
kernel key_val = 0x1
kernel key_val = 0x81
存在问题:可知因按键是机械的,存在抖动,所以存在将按下或松开动作当作了发生了多个动作
用测试代码测试,将刚刚的2行驱动打印屏蔽掉,然后编译并复制third_drv.ko到网络根文件系统下:
#exec 5<&-
#rmmod third_drv
# insmod third_drv.ko
# ./third_drv_test &
# keys_val = 0x2
keys_val = 0x82
keys_val = 0x2
keys_val = 0x2
keys_val = 0x82
keys_val = 0x2
keys_val = 0x82
keys_val = 0x2
keys_val = 0x82
# top
Mem: 6724K used, 54460K free, 0K shrd, 0K buff, 2100K cached
CPU: 0% usr 0% sys 0% nice 99% idle 0% io 0% irq 0% softirq
Load 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来卸载驱动模块