zoukankan      html  css  js  c++  java
  • 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
    ");
        
        key_num = 0;
    
        printk("data = %lx
    ", 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
    ", 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
    ", 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
    ",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
    ", 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!
    ");
        }
        
        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);
  • 相关阅读:
    字符串本质
    常用类
    异常处理
    最终类object 和内部类
    接口
    Java多态
    NIO之五Selector
    NIO之四Buffer
    NIO之三Socket通道
    JAVA NIO(二)Channel通道
  • 原文地址:https://www.cnblogs.com/wulei0630/p/9524633.html
Copyright © 2011-2022 走看看