中断机制读取按键

驱动程序

1、函数 wake_up_interruptible (wait_queue_head_t *q);

功能:唤醒注册到等待队列上的进程

原型:

    #include

    void wake_up_interruptible (wait_queue_head_t *q);

说明:

    唤醒 q 指定的注册在等待队列上的进程。该函数不能直接的立即唤醒进程,而是由调度程序转换上下文,调整为可运行状态。

变量:

q :  等待队列变量指针。

    最近在学习驱动时有一个问题始终想不明白,为什么wait_event_interruptible(button_waitq, ev_press)需要一个全局变量来记住中断的发生?

在驱动中实现读取操作时,使用了

  1. wait_event_interruptible(button_waitq, ev_press);

在中断函数中使用了如下语句唤醒:

  1. ev_press = 1;  //表示中断发生了
  2. wake_up_interruptible(&button_waitq);  //唤醒休眠的进程

这样的话,中断能正确读取到。我分别尝试了屏蔽ev_press = 1;和wake_up_interruptible(&button_waitq);代码,发现中断不能正常产生。

2、函数 wait_event_interruptible(kwait, key_press);

,该函数修改task的状态为TASK_INTERRUPTIBLE,意味着该进程将不会继续运行直到被唤醒,然后被添加到等待队列wq中。

在wait_event_interruptible()中首先判断key_press是不是已经满足,如果条件满足则直接返回0,否则调用__wait_event_interruptible(),并用__ret来存放返回

wait_event_interruptible()将本进程置为可中断的挂起状态,反复检查key_press是否成立,如果成立则退出休眠,如果不成立则继续休眠;条件满足后,即把本进程运行状态置为运行态(此时如果不执行下面的函数 wake_up_interruptible,上面wait_event_interruptible还会继续休眠),并将wait从等待队列中清除掉,从而进程能够调度运行。如果进程当前有异步信号(POSIX的),则返回-ERESTARTSYS。

  1. //唤醒 q 指定的注册在等待队列上的进程。该函数不能直接的立即唤醒进程,而是由调度程序转换上下文,调整为可运行状态。
  2. wake_up_interruptible (wait_queue_head_t *q);

我是这样考虑的:read 调用wait_event_interruptible(button_waitq),我觉得一个参数就够了。不判断什么条件,直接休眠,下面用wake_up_interruptible(&button_waitq)直接唤醒。这样看起来很简单。

3、函数 free_irq(unsigned int irq, void *dev_id);

参数说明:

unsigned int  irq:要卸载的中断号

void  *dev_id:这个是要卸载的中断action下的哪个服务函数, 如果没有 设为NULL

4、函数 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);

分析request_threaded_irq()函数中的各个形参

1>:irq:表示申请的中断号。

2>:handler:表示中断服务例程

3.> thread_fn:中断函数名字

    在 Linux 中,中断具有最高的优先级。不论在任何时刻,只要产生中断事件,内核将立即执行相应的中断处理程序,等到所有挂起的中断和软中断处理完毕后才能执行正常的任务,因此有可能造成实时任务得不到及时的处理。中断线程化之后,中断将作为内核线程运行而且被赋予不同的实时优先级,实时任务可以有比中断线程更高的优先级。这样,具有最高优先级的实时任务就能得到优先处理,即使在严重负载下仍有实时性保证。but,并不是所有的中断都可以被线程化,比如时钟中断,主要用来维护系统时间以及定时器等,其中定时器是操作系统的脉搏,一旦被线程化,就有可能被挂起,这样后果将不堪设想,所以不应当被线程化。 

4>.irqflags:表示中断标志位。

5>.devname:表示请求中断的设备的名称。

6>.dev_id: 对应于request_irq()函数中所传递的第五个参数,可取任意值,但必须唯一能够代表发出中断请求的设备,通常取描述该设备的结构体。 共享中断时所用

中断处理标志irqflags,这里先介绍几个待会要用的:

/*linux-2.6.29/include/linux/interrupt.h*/

#define IRQF_DISABLED 0x00000020 /*中断禁止*/

#define IRQF_SAMPLE_RANDOM 0x00000040 /*供系统产生随机数使用*/

#define IRQF_SHARED 0x00000080 /*在设备之间可共享*/

#define IRQF_PROBE_SHARED 0x00000100/*探测共享中断*/

#define IRQF_TIMER 0x00000200/*专用于时钟中断*/

#define IRQF_PERCPU 0x00000400/*每CPU周期执行中断*/

#define IRQF_NOBALANCING 0x00000800/*复位中断*/

#define IRQF_IRQPOLL 0x00001000/*共享中断中根据注册时间判断*/

#define IRQF_ONESHOT 0x00002000/*硬件中断处理完后触发*/

#define IRQF_TRIGGER_NONE 0x00000000/*无触发中断*/

30 #define IRQF_TRIGGER_RISING 0x00000001 //上升沿触发中断

31 #define IRQF_TRIGGER_FALLING 0x00000002 //下降沿触发中断

32 #define IRQF_TRIGGER_HIGH 0x00000004 //高电平触发中断

33 #define IRQF_TRIGGER_LOW 0x00000008 //低电平触发中断

#define IRQF_TRIGGER_MASK (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \

  IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)

#define IRQF_TRIGGER_PROBE 0x00000010/*触发式检测中断*/

-------------------------- key_irq.c---------------------

#include <linux/init.h>

#include <linux/module.h>

#include <linux/major.h>

#include <linux/cdev.h>

#include <linux/device.h>

#include <linux/types.h>

#include <linux/fs.h>

#include <asm/uaccess.h>

#include <linux/io.h>

#include <linux/gpio.h>

#include <linux/sched.h>

#include <linux/interrupt.h>

#define TAG "my_irq" //修改log

#define INT_GPIO 91 //手机的中断引脚

static int key_major;

static struct cdev key_cdev;

static struct class *key_class;

volatile unsigned long *tlmm_gpio_cfg;

volatile unsigned long *tlmm_in_out;

static int key_press = 0; //按键标志 1为按下

static int irq;

static wait_queue_head_t kwait; //等待队列变量

/*

**中断函数;中断号( int irq )作为你可能在你的 log 消息中打印的信息是有用的, 如果有第二个参数, void *data, 是一类客户数据;

*/

static irqreturn_t key_irq_thread(int irq, void *data) //中断服务程序的入口地址。

{

int value;

printk(TAG" func:%s line:%d\n", __func__, __LINE__);

value = *tlmm_in_out;

value &= 0x1;

if (!value ) {

key_press = 1;

wake_up_interruptible(&kwait); //唤醒注册到等待队列上的进程

}

return IRQ_HANDLED;

}

static ssize_t key_read(struct file *file, char __user *buffer,

size_t count, loff_t *ppos)

{

if(count !=1 )

return -EINVAL;

printk(TAG" func:%s line:%d\n", __func__, __LINE__);

if (!key_press) {

wait_event_interruptible(kwait, key_press); //本进程置为可中断的挂起状态,直到

key_press 为1

}

if(key_press) {

if(!copy_to_user(buffer, &key_press, 1)) {

printk(TAG"%s key is press\n", __func__);

key_press = 0;

} else {

printk(TAG"%s copy to user error\n", __func__);

return -EFAULT;

}

}

return count;

}

ssize_t key_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)

{

printk(TAG" func:%s line:%d\n", __func__, __LINE__);

return count;

}

static int key_open(struct inode *inode, struct file *file)

{

int ret;

printk(TAG" func:%s line:%d\n", __func__, __LINE__);

irq = gpio_to_irq(INT_GPIO);

printk(TAG"%s irq is %d\n", __func__, irq);

ret = request_threaded_irq(irq, NULL, key_irq_thread, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "vol_key", NULL); //注册中断 下降沿触发 函数名字为key_irq_thread

//请求中断的设备名字vol_key

printk(TAG"%s ret is %d\n", __func__, ret);

return ret;

}

static int key_rinit_waitqueue_headelease(struct inode *inode, struct file *file)

{

printk(TAG" func:%s line:%d\n", __func__, __LINE__);

free_irq(irq, NULL); // /*卸载中断

return 0;

}

static const struct file_operations key_ops = {

.owner = THIS_MODULE,

.read = key_read,

.write = key_write,

.open = key_open,

.release = key_release,

};

static int my_key_init(void)

{

int retval;

dev_t dev_id;

printk(TAG" func:%s line:%d\n", __func__, __LINE__);

retval = alloc_chrdev_region(&dev_id, 0, 1, "key_irq"); //0,1

key_major = MAJOR(dev_id);

printk(TAG"major is %d\n", key_major);

if (retval < 0) {

printk(TAG"can't get major number\n");

goto error;

}

cdev_init(&key_cdev, &key_ops);

retval = cdev_add(&key_cdev, dev_id, 1); //1

if (retval < 0) {

printk(TAG"cannot add cdev\n");

goto cleanup_alloc_chrdev_region;

}

key_class = class_create(THIS_MODULE, "key_irq");

if (IS_ERR(key_class)) {

printk(TAG "Error creating key class.\n");

cdev_del(&key_cdev);

retval = PTR_ERR(key_class);

goto cleanup_alloc_chrdev_region;

}

device_create(key_class, NULL, MKDEV(key_major, 0), NULL, "key_irq"); //MKDEV(mytest_major,0),NULL,"mytest");

tlmm_gpio_cfg = (volatile unsigned long *)ioremap(0x105B000, 8); //

tlmm_in_out = tlmm_gpio_cfg + 1; //

*tlmm_gpio_cfg |= 0x3; //

(&kwait);//no //等待队列初始化

return 0;

cleanup_alloc_chrdev_region:

unregister_chrdev_region(dev_id, 1);//1

error:

return retval;

}

static void key_exit(void)

{

dev_t dev_id = MKDEV(key_major, 0);

iounmap(tlmm_gpio_cfg);

device_destroy(key_class, MKDEV(key_major, 0));

class_destroy(key_class);

cdev_del(&key_cdev);

unregister_chrdev_region(dev_id, 1);//1

printk(TAG" func:%s line:%d\n", __func__, __LINE__);

}

module_init(my_key_init);

module_exit(key_exit);

MODULE_LICENSE("GPL");

--------------------------------------------------------end ----------------------------------------

应用程序 需改open 函数的设备名称

查看驱动

查看应用程序

设备节点

猜你喜欢

转载自blog.csdn.net/qq_41831724/article/details/81633509