驱动程序
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)需要一个全局变量来记住中断的发生?
在驱动中实现读取操作时,使用了
- wait_event_interruptible(button_waitq, ev_press);
在中断函数中使用了如下语句唤醒:
- ev_press = 1; //表示中断发生了
- 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。
- //唤醒 q 指定的注册在等待队列上的进程。该函数不能直接的立即唤醒进程,而是由调度程序转换上下文,调整为可运行状态。
- 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 函数的设备名称
查看驱动
查看应用程序
设备节点