Skip to main content
 首页 » 操作系统

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

2022年07月19日25zhujiabin

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

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

转载: 


本文参考链接:https://www.cnblogs.com/hellokitty2/p/9074631.html