中断处理体系结构

在裸板程序按键中断工程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来卸载驱动模块



                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值