阻塞型驱动设计
1.阻塞的必要性
当一个设备无法立刻满足用户的读写请求时应当如何处理? 例如:调用read时,设备没有数据提供, 但以后可能会有;或者一个进程试图向设备写入数据,但是设备暂时没有准备好接收数据。当上述情况发生的时候,驱动程序应当(缺省地)阻塞进程,使它进入等待(睡眠)状态,直到请求可以得到满足。
A准备对B(驱动程序)读取数据,B说现在还没数据。请A先进候车室等待,B之后有了数据。B就将A唤醒。
2.内核等待队列
在实现阻塞驱动的过程中,也需要有一个“候车室”来安排被阻塞的进程“休息”,当唤醒它们的条件成熟时,则可以从“候车室”中将这些进程唤醒。而这个“候车室”就是等待队列。
1、定义等待队列
wait_queue_head_t my_queue
2、初始化等待队列
init_waitqueue_head(&my_queue);
3、定义+初始化等待队列
DECLARE_WAIT_QUEUE_HEAD(my_queue);
3.进入等待队列,睡眠
wait_event(queue,condition)
当condition(布尔表达式)为真时,立即返回;否则让进程进入TASK_UNINTERRUPTIBLE模式的睡眠,并挂在queue参数所指定的等待队列上。
wait_event_interruptible(queue,condition)
当condition(布尔表达式)为真时,立即返回;否则让进程进入TASK_INTERRUPTIBLE的睡眠,并挂在queue参数所指定的等待队列上。
int wait_event_killable(queue, condition)
当condition(一个布尔表达式)为真时,立即返回;否则让进程进入TASK_KILLABLE的睡眠,并挂在queue参数所指定的等待队列上。
4.从等待队列中唤醒进程
wake_up(wait_queue_t *q)
从等待队列q中唤醒状态为TASK_UNINTERRUPTIBLE,TASK_INTERRUPTIBLE,TASK_KILLABLE 的所有进程。
wake_up_interruptible(wait_queue_t *q)
从等待队列q中唤醒状态为TASK_INTERRUPTIBLE 的进程。
5.对按键程序进行改造
key.c
-
#include <linux/module.h>
-
#include <linux/init.h>
-
#include <linux/miscdevice.h>
-
#include <linux/fs.h>
-
#include <asm/uaccess.h>
-
#include <linux/io.h>
-
#include <linux/irqreturn.h>
-
#include <linux/interrupt.h>
-
#include <linux/uaccess.h>
-
-
#define GPFCON (volatile unsigned long*) 0x56000050
-
#define GPFDAT (volatile unsigned long*) 0x56000054
-
-
unsigned int *gpio_data;
-
-
struct work_struct *work1;
-
struct timer_list key_timer;
-
wait_queue_head_t key_q;
-
-
unsigned int key_num = 0;
-
-
void key_timer_func(unsigned long data)
-
{
-
unsigned int key_val;
-
key_val = readw(gpio_data);
-
if((key_val & 0x01) == 0)
-
{
-
key_num = 4;
-
}
-
else if((key_val& 0x02) == 0)
-
{
-
key_num = 1;
-
}
-
else if((key_val & 0x04) == 0)
-
{
-
key_num = 3;
-
}
-
else if((key_val & 0x10) == 0)
-
{
-
key_num = 2;
-
}
-
-
wake_up(&key_q); //唤醒进程
-
}
-
-
void work1_func(struct work_struct *work)
-
{
-
mod_timer(&key_timer, jiffies + HZ/10);
-
}
-
-
irqreturn_t key_int(int irq, void *dev_id)
-
{
-
//1.检测是否发生了按键中断
-
-
//2.清除已经发生的按键中断
-
-
//3.提交下半部
-
schedule_work(work1);
-
-
return 0;
-
}
-
-
void key_hw_init()
-
{
-
unsigned int *gpio_config;
-
unsigned short data;
-
-
gpio_config = ioremap(GPFCON, 4);
-
gpio_data = ioremap(GPFDAT, 4);
-
data = readw(gpio_config);
-
data &= ~0x33f;
-
data |= 0x22a;
-
writew(data, gpio_config);
-
}
-
-
int key_open(struct inode *inode, struct file *filp)
-
{
-
return 0;
-
}
-
-
ssize_t key_read(struct file *filp, char __user *buf, size_t size, loff_t *pos)
-
{
-
wait_event(key_q, key_num); //进入睡眠
-
-
printk("in kernel :key num is %d\n",key_num);
-
copy_to_user(buf, &key_num, 4);
-
-
key_num = 0;
-
-
return 4;
-
}
-
-
struct file_operations key_fops = {
-
.open = key_open,
-
.read = key_read,
-
};
-
-
struct miscdevice key_miscdev = {
-
.minor = 200,
-
.name = "tq2400key",
-
.fops = &key_fops,
-
};
-
-
static int button_init()
-
{
-
int ret;
-
ret = misc_register(&key_miscdev);
-
if(ret != 0)
-
{
-
printk(KERN_ERR"misc_register failed\n");
-
}
-
//按键初始化
-
key_hw_init();
-
-
//注册中断处理程序
-
ret = request_irq(IRQ_EINT0, &key_int, IRQF_TRIGGER_FALLING, "tq2440key", 0);
-
ret = request_irq(IRQ_EINT1, &key_int, IRQF_TRIGGER_FALLING, "tq2440key", 0);
-
ret = request_irq(IRQ_EINT2, &key_int, IRQF_TRIGGER_FALLING, "tq2440key", 0);
-
ret = request_irq(IRQ_EINT4, &key_int, IRQF_TRIGGER_FALLING, "tq2440key", 0);
-
-
//创建工作
-
work1 = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
-
INIT_WORK(work1, work1_func);
-
-
//初始化定时器
-
init_timer(&key_timer);
-
key_timer.function = key_timer_func;
-
-
//注册定时器
-
add_timer(&key_timer);
-
-
//初始化等待队列
-
init_waitqueue_head(&key_q); //初始化等待队列
-
return 0;
-
}
-
-
static void button_exit()
-
{
-
misc_deregister(&key_miscdev);
-
//注销中断处理函数
-
free_irq(IRQ_EINT0, 0);
-
free_irq(IRQ_EINT1, 0);
-
free_irq(IRQ_EINT2, 0);
-
free_irq(IRQ_EINT4, 0);
-
}
-
-
module_init(button_init);
-
module_exit(button_exit);
阅读(474) | 评论(0) | 转发(0) |