Linux设备驱动开发基础--阻塞型设备驱动

1. 当一个设备无法立刻满足用户的读写请求时(例如调用read时,设备没有数据提供),驱动程序应当(缺省的)阻塞进程,使它进入等待(睡眠)状态,知道请求可以得到满足。

 

2. Linux内核等待队列:在实现阻塞驱动的过程中,需要有一个“候车室”来安排被阻塞的进程“休息”,当唤醒它们的条件成熟时,则可以从“候车室”中将这些进程唤醒。而这个“候车室”就是等待队列。

 

3. Linux内核等待队列的使用

(1)定义等待队列

wait_queue_head_t my_queue;

 

(2)初始化等待队列

init_waitqueue_head(&my_queue);

 

(3)定义+初始化等待队列

DECLARE_WAIT_QUEUE_HEAD(my_queue)

 

(4)使进程进入等待队列

① wait_event

wait_event(queue, condition)

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

② wait_event_interruptible

wait_event_interruptible(queue, condition)

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

③ wait_event_killable

wait_event_killable(queue, condition)

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

 

(5)从等待队列中唤醒进程

 ① wake_up:

wake_up(wait_queue_t *q)

  从等待队列q中唤醒状态为TASK_UNINTERRUPTIBLE,TASK_INTERRUPTIBLE,TASK_KILLABLE 的所有进程。

 ② wake_up_interruptible

wake_up_interruptible(wait_queue_t *q)

  从等待队列q中唤醒状态为TASK_INTERRUPTIBLE 的进程

 

4. 简单示例

#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 GPGCON 0x56000060
#define GPGDAT 0x56000064

unsigned int *gpio_config;

struct timer_list buttons_timer;

unsigned int key_num = 0;

wait_queue_head_t  key_wait_queue;

void keys_timer_function(unsigned long data)  
{
    printk("keys_timer_function\n");
    
    key_num = 0;

    printk("data = %lx\n", data);
    
    switch(data)
    {
        case IRQ_EINT8:
            key_num = 1;
            break;
            
        case IRQ_EINT11:
            key_num = 2;
            break;
            
        case IRQ_EINT13:
            key_num = 3;
            break;    
        case IRQ_EINT14:
            key_num = 4;
            break;
        
        case IRQ_EINT15:
            key_num = 5;
            break;
        
        case IRQ_EINT19:
            key_num = 6;
            break;            
        
        default:
            break;
    }

    printk("key_num = %d\n", key_num);
    
    wake_up(&key_wait_queue);
} 

irqreturn_t key_int(int irq, void *dev_id)
{
    //1. 检测是否发生了按键中断
    
    //2. 清除已经发生的按键中断
    
    //3. 提交下半部
    buttons_timer.data = (unsigned long)irq;
    mod_timer(&buttons_timer, jiffies + (HZ / 10)); 
    
    //return 0;
    return IRQ_HANDLED;
}

void key_hw_init(void)
{ 
    unsigned int config_data;
    
    config_data = readl(gpio_config);
    config_data &= 0b00111100;
    config_data |= 0b10000010;

    printk("config_data = %x\n", config_data);
    
    writel(config_data, gpio_config);
}


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_wait_queue, 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(void)
{
    int ret;
    
    gpio_config = ioremap(GPGCON, 4);
    
    ret = misc_register(&key_miscdev);
    
    printk("ret = %d\n", ret);
    
    if (ret == 0)
    {
        //按键初始化
        key_hw_init();
        
        //注册中断处理程序
        request_irq(IRQ_EINT8, key_int, IRQF_TRIGGER_LOW, "key1", 0);
        request_irq(IRQ_EINT11, key_int, IRQF_TRIGGER_LOW, "key2", 0);
        request_irq(IRQ_EINT13, key_int, IRQF_TRIGGER_LOW, "key3", 0);
        request_irq(IRQ_EINT14, key_int, IRQF_TRIGGER_LOW, "key4", 0);
        request_irq(IRQ_EINT15, key_int, IRQF_TRIGGER_LOW, "key5", 0);
        request_irq(IRQ_EINT19, key_int, IRQF_TRIGGER_LOW, "key6", 0);
        
        /* 初始化定时器 */  
        init_timer(&buttons_timer);   
        buttons_timer.function = keys_timer_function;  
        
        /* 向内核注册一个定时器 */  
        add_timer(&buttons_timer);  
        
        /* 初始化等待队列 */
        init_waitqueue_head(&key_wait_queue);
    }
    else
    {
        printk("register fail!\n");
    }
    
    return ret;
}

static void button_exit(void)
{    
    iounmap(gpio_config);
    
    free_irq(IRQ_EINT8, 0);
    free_irq(IRQ_EINT11, 0);
    free_irq(IRQ_EINT13, 0);
    free_irq(IRQ_EINT14, 0);
    free_irq(IRQ_EINT15, 0);
    free_irq(IRQ_EINT19, 0);
    
    misc_deregister(&key_miscdev);    
}

module_init(button_init);
module_exit(button_exit);

 

posted @ 2018-08-23 16:32  99度的水  阅读(481)  评论(0编辑  收藏  举报