阻塞型驱动设计

阻塞的必要性
当一个设备无法立刻满足用户的读写请求时应当如何处理?例如:调用read时,设备没有数据提供,但以后可能会有;或者一个进程试图向设备写入数据,但是设备暂时没有准备好接收数据。当上述情况发生的时候,驱动程序应当(缺省地)阻塞进程,使它进入等待(睡眠)状态,直到请求可以得到满足。


根据驱动的读写规范,驱动的读写都应该按照阻塞模型进行设计。

在实现阻塞型驱动程序中,需要有一个“等待空间”来安排被阻塞的进程来让其进行等待。当唤醒它们的条件成熟时,则可以从中将它们唤醒。这个等待空间就是等待队列。

使用等待队列首先要对其进行定义并初始化
①定义等待队列:wait_queue_head_t my_queue
②初始化等待队列:init_waitqueue_head(&my_queue)
③定义+初始化等待队列:DECLARE_WAIT_QUEUE_HEAD(my_queue)
等待队列的定义与初始化一般在驱动程序的加载函数处完成。

进入等待队列,睡眠的函数有三个:
①wait_enent(queue,condition)
queue:等待队列变量;
当condition(布尔表达式)为真时,立即返回;否则让进程进入TASK_UNINTERRUPTIBLE(不可唤醒、不可打断)模式的睡眠,并挂载queue参数所指定的等待队列上。

②wait_event_interruptible(queue,condition)
queue:等待队列变量;
当condition(布尔表达式)为真时,立即返回;否则让进程进入TASK_INTERRUPTIBLE(可中断)的睡眠模式,并挂在queue参数所指定的等待队列上。

③wait_event_killable(queue,condition)
queue:等待队列变量;
当condition(布尔表达式)为真时,立即返回;否则让进程进入TASK_KILLABLE的睡眠模式,并挂在queue参数所指定的等待队列上。

睡眠函数通常使用在read,write等函数里面,进行检测。若因为condition不为真使得进程进入睡眠模式,则需要执行唤醒函数才能使进程退出睡眠模式继续执行。

从等待队列中唤醒进程:
①wake_up(wait_queue_t *q)
从等待队列q中唤醒状态为TASK_UNINTERRUPTIBLE,TASK_INTERRUPTIBLE,TASK_KILLABLE的所有进程。

②wake_up_interruptible(wait_queue_t *q)
从等待队列q中唤醒状态为TASK_INTERRUPTIBLE的进程。


在中断程序中,唤醒函数通常执行在中断处理函数中。
使用代码:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <asm/uaccess.h>

#define GPFCON 0x56000050
#define GPFDAT 0x56000054

struct work_struct *work1;

struct timer_list buttons_timer;

unsigned int *gpio_data;

unsigned int key_num = 0;

wait_queue_head_t  key_q;

void work1_func(struct work_struct *work)
{
    mod_timer(&buttons_timer, jiffies + (HZ /10));  
}

void buttons_timer_function(unsigned long data)  
{
    unsigned int key_val;

    key_val = readw(gpio_data)&0x1; 
    if (key_val == 0)
       key_num = 4;

    key_val = readw(gpio_data)&0x4;
    if (key_val == 0)
        key_num = 3;

    wake_up(&key_q);

} 

irqreturn_t key_int(int irq, void *dev_id)
{
    //1. 检测是否发生了按键中断

    //2. 清除已经发生的按键中断

    //3. 提交下半部
    schedule_work(work1);

    //return 0;
    return IRQ_HANDLED;     
}

void key_hw_init()
{ 
    unsigned int *gpio_config;
    unsigned short data;

    gpio_config = ioremap(GPFCON,4);
    data = readw(gpio_config);
    data &= ~0b110011;
    data |= 0b100010;

    writew(data,gpio_config);

    gpio_data = ioremap(GPFDAT,4);
}

int key_open(struct inode *node,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 = "key",
    .fops = &key_fops,  
};

static int button_init()
{
    int ret;
    ret = misc_register(&key_miscdev);

    if (ret !=0)
        printk("register fail!\n");

    //注册中断处理程序
    request_irq(IRQ_EINT0,key_int,IRQF_TRIGGER_FALLING,"key",(void *)4);
    request_irq(IRQ_EINT2,key_int,IRQF_TRIGGER_FALLING,"key",(void *)3);

    //按键初始化
    key_hw_init();

    //. 创建工作
    work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
    INIT_WORK(work1, work1_func);

    /* 初始化定时器 */  
    init_timer(&buttons_timer);   
    buttons_timer.function  = buttons_timer_function;  

    /* 向内核注册一个定时器 */  
    add_timer(&buttons_timer);  

    /*初始化等待队列*/
    init_waitqueue_head(&key_q);

    return 0; 
}

static void button_exit()
{
    misc_deregister(&key_miscdev);  
}

module_init(button_init);
module_exit(button_exit);

当应用程序调用read函数读取数据时,内核调用key_read函数,key_read函数在开头就调用等待函数判断是否需要进行进程阻塞,若不需要则进行下一行代码的执行,若需要则阻塞进程,并等待条件满足;当按键按下时产生中断,中断函数最终调用唤醒函数唤醒进程,从而key_read函数得以继续执行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值