LINUX系统中断处理结构及中断函数的实现
中断系统流程解析:
asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
handle_IRQ(irq, regs);
generic_handle_irq(irq);/*Garmen:进行一般的中断处理*/
struct irq_desc *desc = irq_to_desc(irq); /*#define irq_to_desc(irq) (&irq_desc[irq]) Garmen:他是以irq为下标的一个全局数组项*/
generic_handle_irq_desc(irq, desc);
desc->handle_irq(irq, desc); /*Garmen : 那么究竟是谁调用handle_irq ??? 下面进行分析*/
---> 问:所以是谁调用handle_irq ?
答:进行搜索handle_irq然后进入kernel\irq\Chip.c
__irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0);/*Garmen : 以irq为索引,找到一项*/
desc->handle_irq = handle; /*Garmen : 这里进行设置,把上面以irq为索引找到的那项的handle_irq指定为传进来的参数handle*/
然后我们再搜索__irq_set_handler被谁调用:
irq_set_handler(unsigned int irq, irq_flow_handler_t handle)
__irq_set_handler(irq, handle, 0, NULL);
而且我们在linux-3.4.2\arch\arm\plat-s3c24xx\Irq.c 发现:
void __init s3c24xx_init_irq(void)
/*Garmen: 其实下面这些就相当于初始化函数*/
for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++)
irq_set_chip_and_handler(irqno, &s3c_irq_eint0t4,handle_edge_irq); /*Garmen : 再跟进去*/
/*Garmen:irqno对应的就是上面irq_desc的irq索引
*然后你会惊人的发现这个handle_edge_irq 就是handle就是上面desc->handle_irq = handle 中断处理函数
*/
irq_set_chip_and_handler_name(irq, chip,handle, NULL);
irq_set_chip(irq, chip);
desc->irq_data.chip = chip; /*Garmen:chip函数也是一样,有跟进来的irq索引和chip*/
__irq_set_handler(irq, handle, 0, name); /*Garmen : 于是我们惊人的发现这个函数被调用了*/
再研究一下handle_edge_irq
handle_edge_irq:
/* Start handling the irq */
desc->irq_data.chip->irq_ack(&desc->irq_data);
handle_irq_event(desc);
struct irqaction *action = desc->action; /*Garmen : 取出desc中action成员*/
ret = handle_irq_event_percpu(desc, action); /*Garemn : 执行action相关函数*/
小结:问:按下按键之后怎么处理
答:1、进入异常模式:
b vector_irq + 偏移值
2、调用列表,比如调用到irq_usr,保持现场什么的工作
3、调用asm_do_irq
4、调用irq_desc[irq]->handle_irq /*Garmen: 以中断号为下标,取出一项处理函数*/
5、上面的handle_irq 就是一个中断处理函数 , 好比如handle_edge_irq
里面做了什么事情呢?参考上面handle_edge_irq的研究
irq_desc结构体定义及其说明:他是一个全局数组
①:chip是一些芯片底层硬件相关的操作(比如响应中断、清中断等等),在linux3.4.2内核。他在irq_data这个数组里面,这里没有列出来
②:handle_irq是中断处理函数,将aciton链表中的成员一一取出来,然后执行action->handler
/**
* struct irq_desc - interrupt descriptor
* @irq_data: per irq and chip data passed down to chip functions
* @timer_rand_state: pointer to timer rand state struct
* @kstat_irqs: irq stats per cpu
* @handle_irq: highlevel irq-events handler
* @preflow_handler: handler called before the flow handler (currently used by sparc)
* @action: the irq action chain
* @status: status information
* @core_internal_state__do_not_mess_with_it: core internal status information
* @depth: disable-depth, for nested irq_disable() calls
* @wake_depth: enable depth, for multiple irq_set_irq_wake() callers
* @irq_count: stats field to detect stalled irqs
* @last_unhandled: aging timer for unhandled count
* @irqs_unhandled: stats field for spurious unhandled interrupts
* @lock: locking for SMP
* @affinity_hint: hint to user space for preferred irq affinity
* @affinity_notify: context for notification of affinity changes
* @pending_mask: pending rebalanced interrupts
* @threads_oneshot: bitfield to handle shared oneshot threads
* @threads_active: number of irqaction threads currently running
* @wait_for_threads: wait queue for sync_irq to wait for threaded handlers
* @dir: /proc/irq/ procfs entry
* @name: flow handler name for /proc/interrupts output
*/
struct irq_desc {
struct irq_data irq_data;
struct timer_rand_state *timer_rand_state;
unsigned int __percpu *kstat_irqs;
irq_flow_handler_t handle_irq;
#ifdef CONFIG_IRQ_PREFLOW_FASTEOI
irq_preflow_handler_t preflow_handler;
#endif
struct irqaction *action; /* IRQ action list */
unsigned int status_use_accessors;
unsigned int core_internal_state__do_not_mess_with_it;
unsigned int depth; /* nested irq disables */
unsigned int wake_depth; /* nested wake enables */
unsigned int irq_count; /* For detecting broken IRQs */
unsigned long last_unhandled; /* Aging timer for unhandled count */
unsigned int irqs_unhandled;
raw_spinlock_t lock;
struct cpumask *percpu_enabled;
#ifdef CONFIG_SMP
const struct cpumask *affinity_hint;
struct irq_affinity_notify *affinity_notify;
#ifdef CONFIG_GENERIC_PENDING_IRQ
cpumask_var_t pending_mask;
#endif
#endif
unsigned long threads_oneshot;
atomic_t threads_active;
wait_queue_head_t wait_for_threads;
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
struct module *owner;
const char *name;
} ____cacheline_internodealigned_in_smp;
struct irqaction
{
irq_handler_t handler;
unsigned long flags;
void *dev_id;
void __percpu *percpu_dev_id;
struct irqaction *next;
int irq;
irq_handler_t thread_fn;
struct task_struct *thread;
unsigned long thread_flags;
unsigned long thread_mask;
const char *name;
struct proc_dir_entry *dir;
} ____cacheline_internodealigned_in_smp;
分析request_irq函数
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)
return request_threaded_irq(irq, handler, NULL, flags, name, dev);
int request_threaded_irq(unsigned int irq, irq_handler_t handler, irq_handler_t thread_fn, unsigned long irqflags, const char *devname, void *dev_id)
struct irqaction *action;
struct irq_desc *desc;
action = kzalloc(sizeof(struct irqaction), GFP_KERNEL); /*Garmen : 分配设置了action结构*/
if (!action)
return -ENOMEM;
action->handler = handler;
action->thread_fn = thread_fn;
action->flags = irqflags;
action->name = devname;
action->dev_id = dev_id;
desc = irq_to_desc(irq); /*Garmen : 以中断号为下标找到了这个desc全局数组项,将这个desc传递给下面的setup_irq */
retval = __setup_irq(irq, desc, action); /*Garmen : 然后调用__setup_irq 设置中断*/
struct irqaction *old, **old_ptr; /*定义old指针做判断*/
old_ptr = &desc->action;
old = *old_ptr
/*Garmen : 你知道什么是共享中断吗?*/
...
/*Garmen : 对action做一系列判断,比如是否是共享中断啊等等,假如不是,就将我们传进来的action加入aciton链表(具体看上面的图)
*然后desc->chip->settype 将引脚配置中断引脚,还有那些flag什么进行定义
*然后desc->->startup/enable 使能引脚
*/
...
插一小部分相关操作
打开设备: exec 5</dev/buttons /*Garmen : 打开这个设备,把它定位到5去*/
查看设备: ps知道当前设备 -sh是771 然后 ls -l /proc/771/fd
关闭设备: exec 5<&-
删除进程:kill -0 PID号
退出进程:kill -9 PID号
1、中断申请函数:
request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);
函数原型:
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id)
第一个参数:unsigned int irq 可例 : IRQ_EINT0
在Irq.h进行宏定义(include\asm-arm)
在Irq.c 的s3c24xx_init_irq函数中有进行初始化;
中断号是去Irq.h 将该宏展开得到的一个数字
第二个参数:irq_handler_t handler 可例 : buttons_irq 是一个处理函数
第三个参数:unsigned long irqflags 可例:IRQT_BOTHEDGE 双边缘触发
在Irq.h进行宏定义
在Irq.c 的s3c_irqext_type中进行初始化
第四个参数:随意
第五个参数:void *dev_id
它是void型的指针。所以不必顾虑太多;
它只不过是用来在free_irq卸载时候,与fre_irq的参数结合在一起,确定卸载哪一个irq_action 结构
小结:当发生IRQ_EINT0这个中断的时候,就去调用这个buttons_irq中断处理函数,
这个函数static irqreturn_t buttons_irq(int irq, void *dev_id),有两个参数
第一个参数是中断号等于IRQ_EINT0 ,第二个参数是dev_id 等于pins_desc[0]
敲黑板:
后面引入这样的一个方法:将irq、*name、pin、key_val写成一个结构体dev_id
例如
定义一个pin_desc结构体原型:
struct pin_desc{
int irq;
char *name;
unsigned int pin;
unsigned int key_val;
};
编写我们自己想要的结构体:
struct pin_desc pins_desc[4] = {
{IRQ_EINT0, "S2", S3C2410_GPF0, KEY_L},
{IRQ_EINT2, "S3", S3C2410_GPF2, KEY_S},
{IRQ_EINT11, "S4", S3C2410_GPG3, KEY_ENTER},
{IRQ_EINT19, "S5", S3C2410_GPG11, KEY_LEFTSHIFT},
};
我们的注册函数就可以这么写了:
for (i = 0; i < 4; i++)
{
request_irq(pins_desc[i].irq, buttons_irq, IRQT_BOTHEDGE, pins_desc[i].name, &pins_desc[i]);
}
里面所做的事情:
①:分配一个irqaction
②:把这个结构体放入irq_des[irq] <==action链表
③:设置引脚
④:中断使能
2、中断清除函数
free_irq(irq, dev_id)
函数原型:void free_irq(unsigned int irq, void *dev_id)
里面所做的事情
①:出链
②:禁止中断
3、系统函数:s3c2410_gpio_getpin
如何使用:
直接拿GPIO口来判断就行!
if (s3c2410_gpio_getpin (s3c2410_GPG10) == 0)
对寄存器的读取和判断
4、休眠函数!
休眠函数:
函数原型:
#define wait_event_interruptible(wq, condition) \
({ \
int __ret = 0; \
if (!(condition)) \
__wait_event_interruptible(wq, condition, __ret); \
__ret; \
})
里面所做的事情:
先判断condition,假如condition为0的话,就调用__wait_event_interruptible(wq, condition, __ret); 让应用程序进行休眠;
假如!condition,不等于0的话,函数就跳过wait_event_interruptible,向下运行__wait_event_interruptible了!!!
所以我们运用该休眠函数的时候,要在wait_event_interruptible 后面,将condition标志位置1;
例函:
/* 中断事件标志, 中断服务程序将它置1,forth_drv_read将它清0 */
wait_event_interruptible(button_waitq, ev_press);
还要定义static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
意思是:定义一个等待队列头结构体;
唤醒函数:
ev_press = 1; Garmen: 表示中断发生了
wake_up_interruptible(&button_waitq); Garmen::唤醒休眠的进程
参考驱动函数:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
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;
/*Garmen : 中断队列头*/
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
/* 中断事件标志, 中断服务程序将它置1,third_drv_read将它清0 */
static volatile int ev_press = 0;
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},
};
/*
* 确定按键值
*/
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;
}
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为输入引脚 */
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;
/* 如果没有按键动作, 休眠 */
wait_event_interruptible(button_waitq, ev_press);
/* 如果有按键动作, 返回键值 */
copy_to_user(buf, &key_val, 1);
/*Garmen : 同时将休眠标志位至0 这样又可以继续休眠*/
ev_press = 0;
return 1;
}
int third_drv_close(struct inode *inode, struct file *file)
{
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");
在测试程序中调用:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
/* 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!\n");
}
while (1)
{
read(fd, &key_val, 1);
printf("key_val = 0x%x\n", key_val);
}
return 0;
}