Linux 驱动层实现阻塞和非阻塞

linux应用层的函数默认是阻塞型的,但是要想真正实现阻塞,还需要驱动的支持才行。

例:open()、scanf()、fgets()、read()、accept() 等

 

1、默认情形,驱动层不实现阻塞和非阻塞

struct samsung_key{
    int major;
    int irqno;
    struct class *cls;
    struct device *dev;
    struct key_event event;
};
struct samsung_key *key_dev;

ssize_t key_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
{
    int ret;

    ret = copy_to_user(buf, &key_dev->event, count);
    if(ret > 0)
    {
        printk("copy_to_user error\n");
        return -EFAULT;
    }
    memset(&key_dev->event, 0, sizeof(struct key_event));    
    
    return 0;
}

// 中断处理程序,多个按键可以根据 irqno 区分  
irqreturn_t key_irq_handler(int irqno, void *dev_id)  
{  
    int ret;  
  
    printk("------------%s-------------\n", __FUNCTION__);  
      
    ret = gpio_get_value(key_info[i].key_gpio);  
    ret ? (key_dev->key.value = 0) : (key_dev->key.value = 1);  
    printk("key = %d status = %d\n", key_dev->key.name, key_dev->key.value);  
  
    // 返回值一定要是 IRQ_HANDLED  
    return IRQ_HANDLED;  
}  
  
static int __init key_drv_init(void)  
{  
    ... ...  
    key_dev->irqno = IRQ_EINT(2);  
    ret = request_irq(key_dev->irqno, key_irq_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "key_eint", NULL);  
    ... ...  
}  
  
static int __exit key_drv_exit(void)  
{  
    ... ...  
    free_irq(key_dev->irqno,NULL);  
    ... ...  
} 
 

 
// 应用层
fd = open("/dev/key0", O_RDWR);

这种情况下,应用层的 read 会一直不停的读按键值,使用 top 指令查看,发现 cpu 几乎被全部占用。



2、驱动层实现阻塞

struct __wait_queue_head {
spinlock_t lock;
struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;

// 初始化等待队列头
init_waitqueue_head(wait_queue_head_t *q);


// 功能:在特定的时候进行休眠
// 参数1:等待队列头
// 参数2:条件,条件为假,该代码就会阻塞,如果为真,不做任何事情
wait_event_interruptible(wait_queue_head_t q,condition);


// 功能: 资源可达的时候,唤醒
wake_up_interruptible(wait_queue_head_t *q);

 

struct samsung_key{
    int major;
    int irqno;
    struct class *cls;
    struct device *dev;
    struct key_event event;
    unsigned char have_data;
    wait_queue_head_t wq_head;
};
struct samsung_key *key_dev;

ssize_t key_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
{
    int ret;

    // 判断是否阻塞,条件为假,就会阻塞,让出cpu
    wait_event_interruptible(key_dev->wq_head,key_dev->have_data);

    ret = copy_to_user(buf, &key_dev->event, count);
    if(ret > 0)
    {
        printk("copy_to_user error\n");
        return -EFAULT;
    }
    memset(&key_dev->event, 0, sizeof(struct key_event));
    key_dev->have_data = 0;    
    
    return 0;
}

// 中断处理程序,多个按键可以根据 irqno 区分  
irqreturn_t key_irq_handler(int irqno, void *dev_id)  
{  
    int ret;  
  
    printk("------------%s-------------\n", __FUNCTION__);  
      
    ret = gpio_get_value(key_info[i].key_gpio);  
    ret ? (key_dev->key.value = 0) : (key_dev->key.value = 1);  
    printk("key = %d status = %d\n", key_dev->key.name, key_dev->key.value);  

    key_dev->have_data = 1;
    // 唤醒
    wake_up_interruptible(&key_dev->wq_head);
  
    // 返回值一定要是 IRQ_HANDLED  
    return IRQ_HANDLED;  
}  
  
static int __init key_drv_init(void)  
{  
    ... ...  
    key_dev->irqno = IRQ_EINT(2);  
    ret = request_irq(key_dev->irqno, key_irq_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "key_eint", NULL); 
    // 初始化等待队列头
    init_waitqueue_head(&key_dev->wq_head);
    ... ...  
}  
  
static int __exit key_drv_exit(void)  
{  
    ... ...  
    free_irq(key_dev->irqno,NULL);  
    ... ...  
} 

// 应用层
fd = open("/dev/key0", O_RDWR);

实现阻塞后,应用层读不到按键值时就会休眠,让出cpu资源



3、驱动层实现非阻塞

实现非阻塞很容易,只需在读数据前加判断就行,可以单独实现非阻塞,也可以同时兼容阻塞和非阻塞、

 

// 单独实现非阻塞
ssize_t key_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
{
    int ret;

    // 非阻塞,资源不可达的时候,立刻返回,资源可达,直接读写数据
    if( (filp->f_flags & O_NONBLOCK) && !key_dev->have_data)
        return -EAGAIN;

    ret = copy_to_user(buf, &key_dev->event, count);
    if(ret > 0)
    {
        printk("copy_to_user error\n");
        return -EFAULT;
    }
    memset(&key_dev->event, 0, sizeof(struct key_event));
    key_dev->have_data = 0;    
    
    return 0;
}

// 应用层以非阻塞的方式
fd = open("/dev/key0", O_RDWR | O_NONBLOCK);

非阻塞模式下,当应用程序运行后,没有读到按键值,就会收到一个错误(-EAGAIN)返回值。

 

// 驱动层兼容阻塞和非阻塞
ssize_t key_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
{
    int ret;

    // 资源不可达的时候,立刻返回,资源可达,直接读写数据
    if( (filp->f_flags & O_NONBLOCK) && !key_dev->have_data)
        return -EAGAIN;

    // 判断是否阻塞,条件为假,就会阻塞,让出cpu
    wait_event_interruptible(key_dev->wq_head,key_dev->have_data);

    ret = copy_to_user(buf, &key_dev->event, count);
    if(ret > 0)
    {
        printk("copy_to_user error\n");
        return -EFAULT;
    }
    memset(&key_dev->event, 0, sizeof(struct key_event));
    key_dev->have_data = 0;    
    
    return 0;
}

 

posted @ 2017-10-26 14:30  LeeAaron  阅读(1297)  评论(0编辑  收藏  举报