驱动对应用的异步通知机制

Posted hellokitty2

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了驱动对应用的异步通知机制相关的知识,希望对你有一定的参考价值。

                驱动对应用的异步通知机制

1.应用程序需要完成如下三个步骤:

(1)signal(SIGIO, sig_handler);

调用signal函数,让指定的信号SIGIO与处理函数sig_handler对应。

(2)fcntl(fd, F_SET_OWNER, getpid());

指定一个进程作为文件的“属主(filp->owner)”,这样内核才知道信号要发给哪个进程。

(3)f_flags = fcntl(fd, F_GETFL);fcntl(fd, F_SETFL, f_flags | FASYNC);

在设备文件中添加FASYNC标志,驱动中就会调用将要实现的test_fasync函数。

三个步骤执行后,一旦有信号产生,相应的进程就会收到。


2.驱动需要完成下面四个步骤:

(1)定义结构体fasync_struct。

struct fasync_struct *async_queue;

(2)实现test_fasync,把函数fasync_helper将fd,filp和定义的结构体传给内核。

int test_fasync (int fd, struct file *filp, int mode)
{
    struct _test_t *dev = filp->private_data;

    return fasync_helper(fd, filp, mode, &dev->async_queue);
}

(3)当设备可写时,调用函数kill_fasync发送信号SIGIO给内核。

if (dev->async_queue){
    kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
}

 

3.驱动代码

#include <linux/types.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/sched.h> 
#include <linux/wait.h>
#include <linux/uaccess.h>
#include <mach/gpio.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>

struct key_desc{
    unsigned int  pin;
    unsigned char value;
};

static dev_t devno;
static struct cdev cdev;
static struct class* buttons_class;
static struct device* buttons_device;

static wait_queue_head_t button_waitq;
static struct timer_list buttons_timer;

static volatile int pressed = 0;
static unsigned int key_val;

static struct fasync_struct *button_async;

static struct key_desc *irq_pd;

static volatile unsigned long *gph3con;
static volatile unsigned long *gph3dat;


static struct key_desc key_descs[8] = {
    [0] = {
        .pin = S5PV210_GPH2(3),
        .value = 0x00,
    },
    [1] = {
        .pin = S5PV210_GPH2(4),
        .value = 0x01,
    },
    [2] = {
        .pin = S5PV210_GPH2(5),
        .value = 0x02,
    },
    [3] = {
        .pin = S5PV210_GPH2(6),
        .value = 0x03,
    },
    [4] = {
        .pin = S5PV210_GPH2(7),
        .value = 0x04,
    },
};

static void buttons_timer_function(unsigned long data)
{
    struct key_desc * pindesc = irq_pd;
    unsigned int pinval;
    if (!pindesc)
        return;
    pinval = gpio_get_value(pindesc->pin);

    if (pinval)
    {
        /* 松开 */
        key_val = 0x80 | pindesc->value;
    }
    else
    {
        /* 按下 */
        key_val = pindesc->value;
    }
    pressed = 1;
    wake_up_interruptible(&button_waitq);
    
    kill_fasync (&button_async, SIGIO, POLL_IN);
}

static irqreturn_t buttons_irq(int irq, void *dev_id){
    printk("buttons_irq happen\n");
    /* 10ms后启动定时器 */
    irq_pd = (struct key_desc *)dev_id;
    mod_timer(&buttons_timer, jiffies+HZ/100);
    return IRQ_RETVAL(IRQ_HANDLED);
}

static int buttons_open(struct inode *inode, struct file *file){
    int ret;

    ret = request_irq(IRQ_EINT(19),   buttons_irq, IRQ_TYPE_EDGE_BOTH, "key1", &key_descs[0]);
    if(ret)
        return ret;
    ret = request_irq(IRQ_EINT(20),   buttons_irq, IRQ_TYPE_EDGE_BOTH, "key2", &key_descs[1]);
    if(ret)
        return ret;
     ret = request_irq(IRQ_EINT(21),   buttons_irq, IRQ_TYPE_EDGE_BOTH, "key3", &key_descs[2]);
    if(ret)
        return ret;
     ret = request_irq(IRQ_EINT(22),   buttons_irq, IRQ_TYPE_EDGE_BOTH, "key4", &key_descs[3]);
    if(ret)
        return ret;
    ret = request_irq(IRQ_EINT(23),   buttons_irq, IRQ_TYPE_EDGE_BOTH, "key5", &key_descs[4]);
    if(ret)
        return ret;
    return 0;
}

static ssize_t buttons_read(struct file * file, char __user *data, size_t count, loff_t *loff){
    
    if (count != 1)
        return -EINVAL;

    if (file->f_flags & O_NONBLOCK)
    {
        if (!pressed)
            return -EAGAIN;
    }

    wait_event_interruptible(button_waitq, pressed);
    pressed = 0;

    if(copy_to_user(data, &key_val, 1)){
        printk(KERN_ERR "The driver can not copy the data to user area!\n");
        return -ENOMEM;
    }
    
    return 0;
}

static int buttons_close(struct inode *inode, struct file *file){
    free_irq(IRQ_EINT(19),  &key_descs[0]);
    free_irq(IRQ_EINT(20),  &key_descs[1]);    
    free_irq(IRQ_EINT(21),  &key_descs[2]);
    free_irq(IRQ_EINT(22),  &key_descs[3]);
    free_irq(IRQ_EINT(23),  &key_descs[4]);
    return 0;
}

static int buttons_fasync (int fd, struct file *filp, int on)
{
    printk("driver: buttons_fasync\n");
    return fasync_helper (fd, filp, on, &button_async);
}

struct file_operations buttons_ops = {
    .open    = buttons_open,
    .read    = buttons_read,
    .release = buttons_close,
    .fasync     = buttons_fasync,
};

int buttons_init(void){
    int ret;

    init_timer(&buttons_timer);
    buttons_timer.function = buttons_timer_function;
    //buttons_timer.expires  = 0;
    add_timer(&buttons_timer); 
    
    cdev_init(&cdev, &buttons_ops);
    cdev.owner = THIS_MODULE;

    ret = alloc_chrdev_region(&devno, 0, 1, "buttons");
    if(ret){
        printk(KERN_ERR "alloc char device region faild!\n");
        return ret;
    }

    ret = cdev_add(&cdev, devno, 1);
    if(ret){
        printk(KERN_ERR "add char device faild!\n");
        goto add_error;
    }

    buttons_class = class_create(THIS_MODULE, "buttonsdrv");
    if(IS_ERR(buttons_class)){
        printk(KERN_ERR "create class error!\n");
        goto class_error;
    }

    buttons_device = device_create(buttons_class, NULL, devno, NULL, "buttons");
    if(IS_ERR(buttons_device)){
        printk(KERN_ERR "create buttons device error!\n");
        goto device_error;
    }

    init_waitqueue_head(&button_waitq);

    gph3con = (volatile unsigned long *)ioremap(0xE0200C60, 16);
    gph3dat = gph3con + 1;

    *gph3con &= (~0x000000ff);
    *gph3con |=0x11;
    *gph3dat &= ~0x03;    

    
    printk("buttons init\n");
    return 0;

device_error:
    class_destroy(buttons_class);
class_error:
    cdev_del(&cdev);
add_error:
    unregister_chrdev_region(devno,1);

    return -ENODEV;
}

void buttons_exit(void){
    printk("buttons exit\n");
    device_destroy(buttons_class, devno);
    class_destroy(buttons_class);
    cdev_del(&cdev);
    unregister_chrdev_region(devno, 1);
}

module_init(buttons_init);
module_exit(buttons_exit);
MODULE_LICENSE("GPL");

 

4.用户空间代码:

#include <fcntl.h>
#include <stdio.h>
#include <poll.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>


int fd; 

void sig_handler(int signum)
{
    unsigned char key_val;
    read(fd, &key_val, 1); 
    printf("key_val: 0x%x\n", key_val);
}

int main(int argc, char **argv)
{
    unsigned char key_val;
    int ret;
    int Oflags;

    signal(SIGIO, sig_handler);

    fd = open("/dev/buttons", O_RDWR | O_NONBLOCK);
    if (fd < 0){ 
        printf("can‘t open!\n");
        return -1; 
    }   

    fcntl(fd, F_SETOWN, getpid());
    Oflags = fcntl(fd, F_GETFL);
    fcntl(fd, F_SETFL, Oflags | FASYNC);

    while(1);

    return 0;
}

 

5.测试结果:

[210_Liujia]#insmod buttons.ko
[210_Liujia]#./buttons_test
key_val: 0x1
key_val: 0x81
key_val: 0x0
key_val: 0x80
key_val: 0x2
key_val: 0x82
key_val: 0x3
key_val: 0x83
key_val: 0x4
key_val: 0x84
key_val: 0x1
key_val: 0x81
key_val: 0x2
key_val: 0x82
key_val: 0x4
key_val: 0x84
key_val: 0x3
key_val: 0x83

 

 

转载: 

以上是关于驱动对应用的异步通知机制的主要内容,如果未能解决你的问题,请参考以下文章

linux设备驱动中的异步通知机制

字符设备驱动按键异步通知

Linux驱动开发异步通知

Linux驱动开发异步通知

Linux之异步通知机制分析

[架构之路-38]:目标系统 - 系统软件 - Linux OS硬件设备驱动必须熟悉的六大工作机制之(并发与互斥阻塞与非阻塞异步通知)