• 4.字符设备驱动------异步通知


    引入:

    按键驱动方式对比

    1. 查询:一直读,耗资源
    2. 中断: 没有超时机制,当没有中断,read函数一直休眠
    3. poll机制,加入超时机制

    以上3种,都是让应用程序主动去读,本节我们学习异步通知,它的作用就是当驱动层有数据时,主动告诉应用程序,然后应用程序再来读, 这样,应用程序就可以干其它的事情,不必一直读

    比如:kill -9 pid ,其实就是通过发信号杀死进程,kill发数据9给指定id号进程

    进程间发信号

    使用 man signal查看需要头文件 #include <signal.h>测试程序如下如果用在gcc下编译需要为sleep函数添加头文件#include <unistd.h>

    实例函数如下arm-linux-gcc -o test test.c

     1 #include <stdio.h>
     2 #include <signal.h>
     3 #include <unistd.h>
     4 
     5  //typedef void (*sighandler_t)(int); 
     6 void my_signal_fun(int signum)
     7 {
     8     static int cnt=0;
     9     printf("signum=%d ,%d times
    ",signum,++cnt );
    10 
    11 }
    12 
    13 int main(int argc, char const *argv[])
    14 {
    15     signal(SIGUSR1,my_signal_fun);
    16     while(1)
    17     {
    18 
    19         sleep(100);
    20     }
    21     return 0;
    22 }

    测试使用kill -USR1 pid后会打印信号

    目标

    实现异步通知:驱动程序使用信号通知应用程序去读取按键

    要求:

    • 1.应用程序要实现,有:注册信号处理函数,使用signal函数
    • 2.谁来发?驱动来发
    • 3.发给谁?驱动发给应用程序,但应用程序必须告诉驱动PID,
    • 4.怎么发?驱动程序调用kill_fasync函数

    看下别人怎么写的:

    搜索下发送信号的函数kill_fasync,寻找一个字符设备是怎么使用的,在drivers/char/rtc.c中有如下调用

     1 kill_fasync (&rtc_async_queue, SIGIO, POLL_IN);
     2 
     3 //结构体定义如下:
     4 static struct fasync_struct *rtc_async_queue;
     5 struct fasync_struct {
     6     int    magic;
     7     int    fa_fd;
     8     struct    fasync_struct    *fa_next; /* singly linked list */
     9     struct    file         *fa_file;
    10 };

    信号的接受者被定义在fasync_struct中,搜索下其初始化函数,发现函数rtc_fasync被调用如下形式,也就是类似于open、close的形式了

     1 static const struct file_operations rtc_fops = {
     2     .owner        = THIS_MODULE,
     3     .llseek        = no_llseek,
     4     .read        = rtc_read,
     5 #ifdef RTC_IRQ
     6     .poll        = rtc_poll,
     7 #endif
     8     .ioctl        = rtc_ioctl,
     9     .open        = rtc_open,
    10     .release    = rtc_release,
    11     .fasync        = rtc_fasync,
    12 };

    函数的原型如下,这个函数用来初始化结构体,应用程序会最终调用他告知驱动应该信号给哪个pid

    1 static int rtc_fasync (int fd, struct file *filp, int on)
    2 {
    3     return fasync_helper (fd, filp, on, &rtc_async_queue);
    4 }

    也就是是说应用程序通过fasync调用驱动具体的rtc_fasync,这个函数会设置fasync_struct这个结构体,这个结构体会被当做驱动发送信号函数的参数,也就是说应用程序告诉驱动程序其目标。

    步骤:

    1.先写驱动程序,在之前的中断程序上修改 

    1.1定义 异步信号结构体 变量:
    1 static struct fasync_struct * button_async;

     1.2在file_operations结构体添加成员.fasync函数,并写函数

     1 static int fourth_fasync (int fd, struct file *file, int on)
     2 {
     3   return fasync_helper(fd, file, on, & button_async); //初始化button_async结构体,就能使用kill_fasync()了
     4 }
     5 
     6 static struct file_operations third_drv_fops={
     7          .owner = THIS_MODULE,
     8          .open = fourth_drv_open,
     9          .read = fourth _drv_read,
    10        .release= fourth _drv_class,   
    11       .poll = fourth _poll,
    12       .fasync = fourth_fasync             //添加初始化异步信号函数
    13 };

     3.3在buttons_irq中断服务函数里发送信号:

     1 static irqreturn_t buttons_irq(int irq, void *dev_id)
     2 {
     3 
     4     ......
     5     ev_press = 1;    /*表示中断发生了*/
     6     wake_up_interruptible(&button_waitq);    /*去队列button_waitq里面唤醒休眠的进程*/
     7 
     8     /*kill_fasync: 发送信号,发给谁?-->在&button_async中定义*/
     9     /*定义之后,要在fasync_helper中初始化,才可使用*/
    10     kill_fasync (&button_async, SIGIO, POLL_IN);        /*SIGIO表有数据可供读写*/
    11     return IRQ_HANDLED;
    12 }

     驱动程序代码如下:

      1 #include <linux/module.h>
      2 #include <linux/kernel.h>
      3 #include <linux/fs.h>
      4 #include <linux/init.h>
      5 #include <linux/delay.h>
      6 #include <linux/irq.h>
      7 #include <asm/uaccess.h>
      8 #include <asm/irq.h>
      9 #include <asm/io.h>
     10 #include <asm/arch/regs-gpio.h>
     11 #include <asm/hardware.h>
     12 #include <linux/poll.h>
     13 
     14 
     15 static struct class *sixthdrv_class;
     16 static struct class_device    *sixthdrv_class_dev;
     17 
     18 volatile unsigned long *gpfcon;
     19 volatile unsigned long *gpfdat;
     20 
     21 volatile unsigned long *gpgcon;
     22 volatile unsigned long *gpgdat;
     23 
     24 
     25 /*    声明等待队列类型中断 button_wait      */
     26 static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
     27 
     28 /* 中断事件标志, 中断服务程序将它置1,sixth_drv_read将它清0 */
     29 static volatile int ev_press = 0;
     30 
     31 /*    异步信号结构体变量    */
     32 static struct fasync_struct *button_async;
     33 
     34 
     35 struct pin_desc{
     36     unsigned int pin;
     37     unsigned int key_val;
     38 };
     39 
     40 
     41 /* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
     42 /* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
     43 static unsigned char key_val;    /*定义全局变量key_val,保存key状态*/
     44 
     45 struct pin_desc pins_desc[4] = {
     46     {S3C2410_GPF0, 0x01},
     47     {S3C2410_GPF2, 0x02},
     48     {S3C2410_GPG3, 0x03},
     49     {S3C2410_GPG11, 0x04},
     50 };
     51 
     52 //static atomic_t canopen = ATOMIC_INIT(1);     //定义原子变量并初始化为1
     53 
     54 static DECLARE_MUTEX(button_lock);     //定义互斥锁
     55 
     56 /*
     57   * 确定按键值
     58   */
     59 static irqreturn_t buttons_irq(int irq, void *dev_id)
     60 {
     61     struct pin_desc * pindesc = (struct pin_desc *)dev_id;
     62     unsigned int pinval;
     63     
     64     pinval = s3c2410_gpio_getpin(pindesc->pin);
     65 
     66     if (pinval)
     67     {
     68         /* 松开 */
     69         key_val = 0x80 | pindesc->key_val;
     70     }
     71     else
     72     {
     73         /* 按下 */
     74         key_val = pindesc->key_val;
     75     }
     76 
     77     ev_press = 1;  /* 表示中断发生了 */
     78      /*去队列button_waitq里面唤醒休眠的进程*/ 
     79     wake_up_interruptible(&button_waitq);  
     80     /*kill_fasync: 发送信号,发给谁?-->在&button_async中定义*/
     81     kill_fasync (&button_async, SIGIO, POLL_IN);      /*发送SIGIO信号给应用层*/
     82     
     83     return IRQ_RETVAL(IRQ_HANDLED);
     84 }
     85 
     86 static int sixth_drv_open(struct inode *inode, struct file *file)
     87 {
     88 #if 0    
     89     if (!atomic_dec_and_test(&canopen))
     90     {
     91         atomic_inc(&canopen);
     92         return -EBUSY;
     93     }
     94 #endif        
     95 
     96     if (file->f_flags & O_NONBLOCK)
     97     {
     98         if (down_trylock(&button_lock))
     99             return -EBUSY;
    100     }
    101     else
    102     {
    103         /* 获取信号量 */
    104         down(&button_lock);
    105     }
    106 
    107     /* 配置GPF0,2为输入引脚 */
    108     /* 配置GPG3,11为输入引脚 */
    109     request_irq(IRQ_EINT0,  buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);
    110     request_irq(IRQ_EINT2,  buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);
    111     request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]);
    112     request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]);    
    113 
    114     return 0;
    115 }
    116 
    117 ssize_t sixth_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
    118 {
    119     if (size != 1)
    120         return -EINVAL;
    121 
    122     if (file->f_flags & O_NONBLOCK)
    123     {
    124         if (!ev_press)
    125             return -EAGAIN;
    126     }
    127     else
    128     {
    129         /*如果没有按键动作,ev_press = 0, 
    130          *将进程挂在队列里面等待,休眠, 不会执行下面的函数*/
    131         wait_event_interruptible(button_waitq, ev_press);
    132     }
    133 
    134     /*如果有按键动作,ev_press = 1,
    135      *会执行到此函数,并返回键值,将变量清零*/
    136     copy_to_user(buf, &key_val, 1);
    137     ev_press = 0;
    138     
    139     return 1;
    140 }
    141 
    142 
    143 int sixth_drv_close(struct inode *inode, struct file *file)
    144 {
    145     //atomic_inc(&canopen);
    146     free_irq(IRQ_EINT0, &pins_desc[0]);
    147     free_irq(IRQ_EINT2, &pins_desc[1]);
    148     free_irq(IRQ_EINT11, &pins_desc[2]);
    149     free_irq(IRQ_EINT19, &pins_desc[3]);
    150     up(&button_lock);
    151     return 0;
    152 }
    153 
    154 static unsigned sixth_drv_poll(struct file *file, poll_table *wait)
    155 {
    156     unsigned int mask = 0;
    157     poll_wait(file, &button_waitq, wait); // 不会立即休眠
    158 
    159     if (ev_press)
    160         mask |= POLLIN | POLLRDNORM;
    161 
    162     return mask;
    163 }
    164 
    165 static int sixth_drv_fasync (int fd, struct file *filp, int on)
    166 {
    167     printk("driver: sixth_drv_fasync
    ");
    168     
    169     //初始化button_async结构体,就能使用kill_fasync()了
    170     return fasync_helper (fd, filp, on, &button_async);
    171 }
    172 
    173 
    174 static struct file_operations sencod_drv_fops = {
    175     .owner   =  THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    176     .open    =  sixth_drv_open,     
    177     .read     =    sixth_drv_read,       
    178     .release =  sixth_drv_close,
    179     .poll    =  sixth_drv_poll,
    180     .fasync     =  sixth_drv_fasync,    //初始化异步信号函数
    181 };
    182 
    183 
    184 int major;    /*定义全局变量,存储主设备号*/
    185 static int sixth_drv_init(void)
    186 {
    187     major = register_chrdev(0, "sixth_drv", &sencod_drv_fops);
    188      /*先创建一个类class,再在class下面创建一个设备(如下)*/
    189     sixthdrv_class = class_create(THIS_MODULE, "sixth_drv");
    190 
    191     sixthdrv_class_dev = class_device_create(sixthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */
    192     /*建立地址映射*/
    193     gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
    194     gpfdat = gpfcon + 1;
    195 
    196     gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);
    197     gpgdat = gpgcon + 1;
    198 
    199     return 0;
    200 }
    201 
    202 static void sixth_drv_exit(void)
    203 {
    204     unregister_chrdev(major, "sixth_drv");
    205     class_device_unregister(sixthdrv_class_dev);
    206     class_destroy(sixthdrv_class);
    207     iounmap(gpfcon);
    208     iounmap(gpgcon);
    209     return 0;
    210 }
    211 
    212 
    213 module_init(sixth_drv_init);
    214 
    215 module_exit(sixth_drv_exit);
    216 
    217 MODULE_LICENSE("GPL");
    sixthdrv.c

    2.写应用测试程序

    步骤如下:

    1) signal(SIGIO, my_signal_fun);  

      调用signal函数,当接收到SIGIO信号就进入my_signal_fun函数,读取驱动层的数据

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

      指定进程做为fd文件的”属主”,内核收到F_SETOWN命令,就会设置pid(驱动无需处理),这样fd驱动程序就知道发给哪个进程

    3) oflags=fcntl(fd,F_GETFL);

      获取fd的文件状态标志

    4) fcntl(fd,F_SETFL, oflags| FASYNC );

      添加FASYNC状态标志,会调用驱动中成员.fasync函数,执行fasync_helper()来初始化异步信号结构体

      这4个步骤执行后,一旦有驱动层有SIGIO信号时,进程就会收到

    应用层代码如下:

     1 #include <sys/types.h>    
     2 #include <sys/stat.h>    
     3 #include <stdio.h>
     4 #include <string.h>
     5 #include <poll.h>               
     6 #include <signal.h>
     7 #include <unistd.h>
     8 #include <fcntl.h>
     9 
    10 int fd,ret;
    11 void my_signal_fun(int signame)      //有信号来了
    12 {
    13    read( fd, &ret, 1);              //读取驱动层数据
    14    printf("key_vale=0X%x
    ",ret); 
    15 
    16 }
    17  
    18 /*useg:    fourthtext   */
    19 int main(int argc,char **argv)
    20 {
    21   int oflag;
    22   unsigned int val=0;      
    23   fd=open("/dev/buttons",O_RDWR); 
    24   if(fd<0)
    25         {printf("can't open!!!
    ");
    26        return -1;}
    27 
    28    signal(SIGIO,my_signal_fun); //指定的信号SIGIO与处理函数my_signal_run对应
    29    fcntl( fd, F_SETOWN, getip());  //指定进程作为fd 的属主,发送pid给驱动
    30    oflag=fcntl( fd, F_GETFL);   //获取fd的文件标志状态
    31    fcntl( fd, F_SETFL, oflag|FASYNC);  //添加FASYNC状态标志,调用驱动层.fasync成员函数 
    32 
    33    while(1)
    34    {
    35          sleep(1000);                  //做其它的事情
    36    }
    37    return 0;
    38 
    39 }
    forth_test.c

    3.测试

    可以发现打开文件的时候会有提示driver: drv_fasync,说明App会主动调用.fasync

     1 # insmod dri.ko
     2 # 后台运行
     3 # ./test &
     4 # driver: drv_fasync
     5 #实际休眠状态的
     6 
     7 # ps
     8   PID  Uid        VSZ Stat Command
     9   798 0          1308 S   ./test
    10 ....
    11 
    12 # 按键按下正常打印
    13 # irq55
    14 key_val: 0x3
    15 irq55
    16 key_val: 0x83
    17 irq18

    参考:

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

    按键之使用异步通知(详解)

  • 相关阅读:
    IIS 添加二级应用程序
    VS中发布并调试IIS程序
    未启用当前数据库的 SQL Server Service Broker,因此查询通知不受支持。如果希望使用通知,请为此数据库启用 Service Broker
    Flash基础开发习惯指要
    2012云计算扫盲
    flash问题集锦(新手必看)
    Flash常用ActionScript控制语句基本用法祥解
    通过offset值的设置使html元素对齐
    不用float也可以让div横向显示
    QQ空间里写的开发心得
  • 原文地址:https://www.cnblogs.com/y4247464/p/10110160.html
Copyright © 2020-2023  润新知