Linux驱动移植14之中断上下部实现按键事件

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很合适。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值