1.中断的上下半部
1.1中断处理的注意点
1)中断上下文,不能和用户空间数据交互
2)不能交出CPU(不能休眠、不能schedule)
3)ISR运行时间尽可能短,越长则系统响应特性越差
1.2中断下半部2种解决方案
1)为什么要分上半部(top half,又叫顶半部)和下半部(bottom half,又叫底半部)
上半部:登记、标记中断,调用下半部
下半部:执行中断处理函数
紧急的时间短,先去处理。不紧急的放到下半部处理。
2)下半部处理策略1:tasklet(小任务)
3)下半部处理策略2:workqueue(工作队列)
下半部的两种方式,区别在于调度的方式。
1.3.tasklet使用实战
1)tasklet相关接口介绍
00464: #define DECLARE_TASKLET(name, func, data) \
00465: struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0) , func, data }
00466:
00467: #define DECLARE_TASKLET_DISABLED(name, func, data) \struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }
00455: struct tasklet_struct
00456: {
00457: struct tasklet_struct *next;
00458: unsigned long state;
00459: atomic_t count;
00460: void (*func)(unsigned long);
00461: unsigned long data;
00462: };
2)实战演示tasklet实现下半部
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <plat/gpio-cfg.h>
#include <linux/gpio.h>
#include <mach/irqs.h> //arch/arm/mach-s5pv210/include/mach/irqs.h
/*
* X210:
*
* POWER -> EINT1 -> GPH0_1
* LEFT -> EINT2 -> GPH0_2
* DOWN -> EINT3 -> GPH0_3
* UP -> KP_COL0 -> GPH2_0
* RIGHT -> KP_COL1 -> GPH2_1
* MENU -> KP_COL3 -> GPH2_3 (KEY_A)
* BACK -> KP_COL2 -> GPH2_2 (KEY_B)
*/
static struct input_dev *button_dev;
#define BUTTON_IRQ IRQ_EINT2
void func(unsigned long data) //被调度的下半部
{
int flag;
printk("keyintrupt: this is bottom part!.\n");
s3c_gpio_cfgpin(S5PV210_GPH0(2), S3C_GPIO_SFN(0));
flag = gpio_get_value(S5PV210_GPH0(2));
s3c_gpio_cfgpin(S5PV210_GPH0(2), S3C_GPIO_SFN(0x0f));
input_report_key(button_dev, KEY_LEFT, !flag); //上报中断处理事件
input_sync(button_dev); //同步
}
DECLARE_TASKLET(mytasklet, func, 0);
static irqreturn_t button_interrupt(int irq, void *dummy)
{
printk("keyintrupt: this is top part!.\n");
tasklet_schedule(&mytasklet);
return IRQ_HANDLED;
}
static int __init button_init(void)
{
int error;
int ret;
ret = gpio_request(S5PV210_GPH0(2), "GPH0_2");
if(ret)
printk("keyintrupt: request gpio GPH0(2) fail");
s3c_gpio_cfgpin(S5PV210_GPH0(2), S3C_GPIO_SFN(0x0f));
if (request_irq(BUTTON_IRQ, button_interrupt, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING , "button-x210", NULL)) //申请了一个中断号并绑定中断处理函数,上升沿和下降沿触发
{
printk(KERN_ERR "keyintrupt.c: Can't allocate irq %d\n", BUTTON_IRQ);
return -EBUSY;
}
button_dev = input_allocate_device();
if (!button_dev)
{
printk(KERN_ERR "keyintrupt.c: Not enough memory\n");
error = -ENOMEM;
goto err_free_irq;
}
button_dev->evbit[0] = BIT_MASK(EV_KEY);
button_dev->keybit[BIT_WORD(KEY_LEFT)] = BIT_MASK(KEY_LEFT);
error = input_register_device(button_dev);
if (error)
{
printk(KERN_ERR "keyintrupt.c: Failed to register device\n");
goto err_free_dev;
}
return 0;
err_free_dev:
input_free_device(button_dev);
err_free_irq:
free_irq(BUTTON_IRQ, button_interrupt);
return error;
}
static void __exit button_exit(void)
{
input_unregister_device(button_dev);
free_irq(BUTTON_IRQ, button_interrupt);
}
module_init(button_init);
module_exit(button_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("jyt<jyt@gmail.com>");
MODULE_DESCRIPTION("Keyboard driver for x210 button.");
MODULE_ALIAS("platform:s3c-button");
3)验证,将驱动编译成模块,装载,验证
4)使用应用层调用验证
2.中断的上下半部之workqueue
2.1.workqueue实战演示
1)workqueue的突出特点是下半部会交给worker thead,因此下半部处于进程上下文,可以被调度,因此可以睡眠。
2)include/linux/workqueue.h
00079: #define DECLARE_WORK(n, f) \
00080: struct work_struct n = __WORK_INITIALIZER(n, f)
00067: #define __WORK_INITIALIZER(n, f) { \
00068: .data = WORK_DATA_STATIC_INIT(), \
00069: .entry = { &(n).entry, &(n).entry }, \
00070: .func = (f), \
00071: __WORK_INIT_LOCKDEP_MAP(#n, &(n)) \
00072: }
void func(struct work_struct *work) //被调度的下半部,workqueue
{
int flag;
printk("keyintrupt: schedule_work,this is bottom part!.\n");
s3c_gpio_cfgpin(S5PV210_GPH0(2), S3C_GPIO_SFN(0));
flag = gpio_get_value(S5PV210_GPH0(2));
s3c_gpio_cfgpin(S5PV210_GPH0(2), S3C_GPIO_SFN(0x0f));
input_report_key(button_dev, KEY_LEFT, !flag); //上报中断处理事件
input_sync(button_dev); //同步
}
//DECLARE_TASKLET(mytasklet, func, 0);
DECLARE_WORK(mywork, func);
static irqreturn_t button_interrupt(int irq, void *dummy)
{
printk("keyintrupt: schedule_work,this is top part!.\n");
//tasklet_schedule(&mytasklet);
schedule_work(&mywork);
return IRQ_HANDLED;
}
2.2中断上下半部处理原则
使用了调度,中断下半部分不会立即执行,操作系统回来处理。
1)必须立即进行紧急处理的极少量任务放入在中断的顶半部中,此时屏蔽了与自己同类型的中断,由于任务量少,所以可以迅速不受打扰地处理完紧急任务。
2)需要较少时间的中等数量的急迫任务放在tasklet中。此时不会屏蔽任何中断(包括与自己的顶半部同类型的中断),所以不影响顶半部对紧急事务的处理;同时又不会进行用户进程调度,从而保证了自己急迫任务得以迅速完成。
3)需要较多时间且并不急迫(允许被操作系统剥夺运行权)的大量任务放在workqueue中。此时操作系统会尽量快速处理完这个任务,但如果任务量太大,期间操作系统也会有机会调度别的用户进程运行,从而保证不会因为这个任务需要运行时间将其它用户进程无法进行。
4)可能引起睡眠的任务放在workqueue中。因为在workqueue中睡眠是安全的。在需要获得大量的内存时、在需要获取信号量时,在需要执行阻塞式的I/O操作时,用workqueue很合适。