• Linux的fasync驱动异步通知详解【转】


    本文转载自:http://blog.csdn.net/coding__madman/article/details/51851338

    工作项目用有个需求是监测某个GPIO输入方波的频率!通俗的讲就是一个最最简单的测方波频率的示波器!不过只是测方波的频率!频率范围是0~200HZ,而且频率方波不是一直都是200HZ,大多数的时候可能一直是0或者一个更低频率的方波!同时要考虑到方波有可能一直维持在200HZ ,同时保持效率和性能的情况下,fasync驱动异步通知是个不错的选择,当初写demo的时候实测1K的方波完全没有问题!应用到项目中也是完全能满足需求!驱动很简单!业余时间把自己之前学到的知识总结一下!对自己也是个提高!

    根据需求,驱动中实现比较简单!自己只实现open、close、fasync和read函数 ,这里只需要读取方波的频率即可!

    驱动大概实现原理:方波每产生一个下降沿,产生一个中断,然后根据中断在通过异步通知应用程序,以此来测定输入方波的频率!

    fansync机制的优势是能使驱动的读写和应用程序的读写分开,使得应用程序可以在驱动读写的时候去做别的事情!

    下面是驱动的源码:

    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. **-------File Info---------------------------------------------------------------------------------------  
    2. ** File Name:               gpioInt.c  
    3. ** Latest modified Data:    2015_11_16  
    4. ** Latest Version:          v1.0  
    5. ** Description:             NOME  
    6. **  
    7. **--------------------------------------------------------------------------------------------------------  
    8. ** Create By:               K  
    9. ** Create date:             20015-11-16  
    10. ** Version:                 v1.0  
    11. ** Descriptions:            混杂设备驱动程序 GPIO中断驱动 下降沿触发GPIO 内核会向用户空间发送一个键值  
    12. **                          用户态的应用程序通过读取键值来判断GPIO中断状况  
    13. **  
    14. **--------------------------------------------------------------------------------------------------------  
    15. *********************************************************************************************************/  
    16. #include<linux/init.h>  
    17. #include<linux/module.h>  
    18. #include<mach/gpio.h>                                                    
    19. #include<asm/io.h>                                                   
    20. #include"mach/../../mx28_pins.h"  
    21. #include <mach/pinctrl.h>  
    22. #include "mach/mx28.h"  
    23. #include<linux/fs.h>  
    24. #include <linux/io.h>  
    25. #include<asm/uaccess.h>                                       
    26. #include<linux/miscdevice.h>                            
    27. #include<linux/irq.h>                            
    28. #include<linux/sched.h>                     
    29. #include<linux/interrupt.h>                
    30. #include<linux/timer.h>  
    31. #include <linux/kernel.h>  
    32. #include <linux/delay.h>  
    33. #include <asm/uaccess.h>  
    34. #include <asm/io.h>  
    35.   
    36. /*  
    37. *中断事件标志,中断服务程序将它置1,在gpio_drv_read将它清0  
    38. */  
    39. static volatile int ev_press = 0;  
    40.   
    41. /* 
    42. *异步结构体指针 用于读 
    43. */  
    44. static struct fasync_struct *b_async;  
    45.   
    46. /* 
    47. *中断引脚描述结构体 
    48. */  
    49. struct pin_desc_s{                
    50.     unsigned int pin;  
    51.     unsigned int key_val;  
    52.     unsigned int irq;  
    53. };  
    54. static unsigned char key_val;  
    55.   
    56. struct pin_desc_s pin_desc[5] = {  
    57.     {MXS_PIN_TO_GPIO(PINID_LCD_ENABLE),0x03,},    /* IO1 rain GPIO1_31     */    
    58.     {MXS_PIN_TO_GPIO(PINID_LCD_HSYNC),0x05,},     /* IO2 windspeed GPIO1_29*/  
    59.     {MXS_PIN_TO_GPIO(PINID_LCD_DOTCK),0x0A,},     /* 机箱门             */  
    60.     {MXS_PIN_TO_GPIO(PINID_AUART3_RX),0x07,},     /* key1 GPIO3_12         */   
    61.     {MXS_PIN_TO_GPIO(PINID_AUART3_TX),0x09,},     /* key2 GPIO3_13         */   
    62. };  
    63.   
    64.   
    65. static DECLARE_MUTEX(b_lock);       
    66. static DECLARE_WAIT_QUEUE_HEAD(b_waitq);  
    67.   
    68.   
    69. static irqreturn_t b_irq(int irq, void *dev_id)  
    70. {  
    71.     struct pin_desc_s * pindesc = (struct pin_desc_s *)dev_id;  
    72.     unsigned int pinval;  
    73.       
    74.     pinval = gpio_get_value(pindesc->pin);  
    75.   
    76.     if (pinval)  
    77.     {  
    78.         key_val = 1;  
    79.     }  
    80.     else  
    81.     {  
    82.         key_val = pindesc->key_val;  
    83.     }  
    84.         ev_press = 1;  
    85.         wake_up_interruptible(&b_waitq);        //唤醒等待队列里面的进程  
    86.         kill_fasync(&b_async, SIGIO, POLL_IN);  //异步通知  
    87.     //printk("interrupt occur.......... ");  
    88.     return IRQ_RETVAL(IRQ_HANDLED);  
    89. }  
    90.   
    91. static int gpio_drv_open(struct inode *inode, struct file *file)  
    92. {  
    93.     int iRet[5]={0};  
    94.     int i = 0;  
    95.   
    96.     if (file->f_flags & O_NONBLOCK)  
    97.     {  
    98.         if (down_trylock(&b_lock))  
    99.             return -EBUSY;  
    100.     }  
    101.     else  
    102.     {  
    103.         down(&b_lock);  
    104.     }  
    105.       
    106.       
    107.     for(i = 0; i < 5; i++)  
    108.     {  
    109.         gpio_direction_input((pin_desc[i]).pin);  
    110.         (pin_desc[i]).irq = gpio_to_irq((pin_desc[i]).pin);   
    111.         if ((pin_desc[i]).irq)   
    112.             disable_irq((pin_desc[i]).irq);  
    113.         set_irq_type((pin_desc[i]).irq, IRQF_TRIGGER_FALLING);  //下降沿中断  
    114.         iRet[i] = request_irq((pin_desc[i]).irq, buttons_irq, IRQF_SHARED, "gpio_int", &pin_desc[i]);  
    115.         if (iRet[i] != 0){  
    116.             printk("request irq failed!! ret: %d  irq:%d  ", iRet[i],(pin_desc[i]).irq);  
    117.         return -EBUSY;}  
    118.       
    119.     }  
    120.       
    121.       
    122.     return 0;  
    123. }  
    124.   
    125. ssize_t gpio_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)  
    126. {  
    127.     if (size != 1)  
    128.         return -EINVAL;  
    129.   
    130.     if (file->f_flags & O_NONBLOCK)  
    131.     {  
    132.         if (!ev_press)  
    133.             return -EAGAIN;  
    134.     }  
    135.     else  
    136.     {  
    137.         wait_event_interruptible(b_waitq, ev_press);  
    138.     }  
    139.     copy_to_user(buf, &key_val, 1);  
    140.     ev_press = 0;  
    141.       
    142.     return 1;  
    143. }  
    144.   
    145. int gpio_drv_close(struct inode *inode, struct file *file)  
    146. {  
    147.     int i = 0;  
    148.       
    149.     for( i = 0; i < 5; i++)  
    150.     {  
    151.         free_irq((pin_desc[i]).irq, &pin_desc[i]);  
    152.     }  
    153.   
    154.     up(&b_lock);  
    155.     return 0;  
    156. }  
    157.   
    158. static int gpio_drv_fasync (int fd, struct file *filp, int on)  
    159. {  
    160.     printk("driver: gpio_drv_successful ");  
    161.     return fasync_helper (fd, filp, on, &b_async);  
    162. }  
    163.   
    164. static struct file_operations gpio_drv_fops = {  
    165.     .owner      = THIS_MODULE,  
    166.     .open       = gpio_drv_open,  
    167.     .read       = gpio_drv_read,  
    168.     .release    = gpio_drv_close,  
    169.     .fasync     = gpio_drv_fasync,  
    170. };  
    171.   
    172. static struct miscdevice b_miscdev =   
    173. {  
    174.     .minor          = MISC_DYNAMIC_MINOR,  
    175.     .name           = "magic-gpio",  
    176.     .fops           = &gpio_drv_fops,  
    177. };  
    178.   
    179. static int __init gpio_drv_init(void)  
    180. {  
    181.     int iRet=0;  
    182.     printk("gpio_miscdev module init! ");  
    183.     iRet = misc_register(&b_miscdev);  
    184.     if (iRet) {  
    185.         printk("register failed! ");  
    186.     }   
    187.     return 0;  
    188. }  
    189.   
    190. static void __exit gpio_drv_exit(void)  
    191. {  
    192.     printk("gpio_miscdev module exit! ");  
    193.     misc_deregister(&b_miscdev);  
    194. }  
    195.   
    196. module_init(gpio_drv_init);  
    197. module_exit(gpio_drv_exit);  
    198.   
    199. MODULE_AUTHOR("HEHAI & RK");  
    200. MODULE_LICENSE("Dual BSD/GPL");  
    201. MODULE_DESCRIPTION("gpio interrupt module");  

    首先还是先从init函数来总结:该驱动是一混杂设备驱动模型来写的,这个主要是借鉴网上的好多资料都是一这种模式来写的,Linux里面misc混杂设备驱动的主设备号是为10的驱动设备,init模块首先是用 misc_register()函数注册一个一个混杂设备驱动,参数一个混杂设备驱动里面非常重要的一个数据结构 struct miscdevice,下面把原型贴出来:
    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. struct miscdevice  {  
    2.     int minor;  
    3.     const char *name;  
    4.     const struct file_operations *fops;  
    5.     struct list_head list;  
    6.     struct device *parent;  
    7.     struct device *this_device;  
    8.     const char *nodename;  
    9.     mode_t mode;  
    10. };  
    当然我上面的驱动代码只初始化了前面的关键三项:
    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. static struct miscdevice b_miscdev =   
    2. {  
    3.     .minor          = MISC_DYNAMIC_MINOR,  
    4.     .name           = "magic-gpio",  
    5.         .fops           = &gpio_drv_fops,  
    6. };  

    这里先说说 .minor这个成员:定义次设备号的,这里使用了一个MISC_DYNAMIC_MINOR宏! 这个宏的意思就是动态分配次设备号!而且这个次设备号不会超过64!实现的方法比较巧妙!这里贴出一篇相关的文章:

    http://blog.csdn.NET/yongan1006/article/details/6778285 这个可以研究一下,还比较有意思!

    剩下的两个name 和 fops成员对驱动开发来说就最熟悉不过了!驱动的名字和驱动的接口函数这里就不说了!

    注册混杂设备驱动后就是接口函数的表演了!

    这里和内核硬件相关的就是struct pin_desc_s 结构了,硬件的初始化工作比较简单,放在open函数里面了!

    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. struct pin_desc_s pin_desc[5] = {  
    2.     {MXS_PIN_TO_GPIO(PINID_LCD_ENABLE),0x03,},    /* IO1 rain GPIO1_31     */    
    3.     {MXS_PIN_TO_GPIO(PINID_LCD_HSYNC),0x05,},     /* IO2 windspeed GPIO1_29*/  
    4.     {MXS_PIN_TO_GPIO(PINID_LCD_DOTCK),0x0A,},     /* 机箱门             */  
    5.     {MXS_PIN_TO_GPIO(PINID_AUART3_RX),0x07,},     /* key1 GPIO3_12         */   
    6.     {MXS_PIN_TO_GPIO(PINID_AUART3_TX),0x09,},     /* key2 GPIO3_13         */   
    7. };  

    这里把好几个gpio接口都放到这一个里面了!都是后边加进去的!上面的是直接根据文档在内核头文件中找到GPIO引脚对应的宏定义的!后边是给GPIO设置的键值!就是当应用程序收到一个signal后,根据读取到的键值来区分是哪一个GPIO发生了中断或是有信号传过来!看看open函数:
    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. static int gpio_drv_open(struct inode *inode, struct file *file)  
    2. {  
    3.     int iRet[5]={0};  
    4.     int i = 0;  
    5.   
    6.     if (file->f_flags & O_NONBLOCK)//非阻塞  
    7.     {  
    8.         if (down_trylock(&b_lock))  
    9.             return -EBUSY;  
    10.     }  
    11.     else  
    12.     {  
    13.         down(&b_lock);  
    14.     }  
    15.       
    16.       
    17.     for(i = 0; i < 5; i++)  
    18.     {  
    19.         gpio_direction_input((pin_desc[i]).pin);//设置对应的GPIO输入  
    20.         (pin_desc[i]).irq = gpio_to_irq((pin_desc[i]).pin);//把GPIO对应的pin值转换为相应的IRQ值并返回  
    21.         if ((pin_desc[i]).irq)   
    22.             disable_irq((pin_desc[i]).irq);//先关闭中断并等待中断处理完  
    23.         set_irq_type((pin_desc[i]).irq, IRQF_TRIGGER_FALLING);  //设置下降沿中断  
    24.         iRet[i] = request_irq((pin_desc[i]).irq, b_irq, IRQF_SHARED, "gpio_int", &pin_desc[i]);  
    25.         if (iRet[i] != 0){  
    26.             printk("request irq failed!! ret: %d  irq:%d  ", iRet[i],(pin_desc[i]).irq);  
    27.         return -EBUSY;}  
    28.       
    29.     }  
    30.       
    31.       
    32.     return 0;  
    33. }  
    这里可以详细了解一下关于GPIO的一些API函数:http://blog.sina.com.cn/s/blog_a6559d9201015vx9.html

    request_irq函数:http://blog.csdn.net/wealoong/article/details/7566546
    说说上面的request_irq函数了:

    int request_irq(unsigned int irq, irq_handler_t handler,
                             unsigned long irqflags, const char *devname, void *dev_id)
    irq是要申请的硬件中断号。
    handler是向系统注册的中断处理函数,是一个回调函数,中断发生时,系统调用这个函数,dev_id参数将被传递给它。
    irqflags是中断处理的属性,SA_SHARED表示多个设备共享中断,
    devname设置中断名称,通常是设备驱动程序的名称  在cat /proc/interrupts中可以看到此名称。
    dev_id在中断共享时会用到,一般设置为这个设备的设备结构体或者NULL。
    request_irq()返回0表示成功,返回-INVAL表示中断号无效或处理函数指针为NULL,返回-EBUSY表示中断已经被占用且不能共享。

    这里用到回调函数b_irq函数就是根据响应的GPIO中断返回设置好的相应的值,这样应用程序在得到这个值的时候就可以知道是哪个GPIO发送的中断!

    b_irq函数:

    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. static irqreturn_t b_irq(int irq, void *dev_id)  
    2. {  
    3.     struct pin_desc_s * pindesc = (struct pin_desc_s *)dev_id;  
    4.     unsigned int pinval;  
    5.       
    6.     pinval = gpio_get_value(pindesc->pin);  
    7.   
    8.     if (pinval)  
    9.     {  
    10.         key_val = 1;  
    11.     }  
    12.     else  
    13.     {  
    14.         key_val = pindesc->key_val;  
    15.     }  
    16.         ev_press = 1;  
    17.         wake_up_interruptible(&b_waitq);        //唤醒等待队列里面的进程  
    18.         kill_fasync(&b_async, SIGIO, POLL_IN);  //异步通知  
    19.     //printk("interrupt occur.......... ");  
    20.     return IRQ_RETVAL(IRQ_HANDLED);  
    21. }  
    其中上面的b_waitq是这样定义的:
    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. static DECLARE_WAIT_QUEUE_HEAD(b_waitq);//生成一个等待队列的头 名字为b_waitq  
    关于等待队列可以看下这篇文章:http://www.cnblogs.com/xmphoenix/archive/2011/11/20/2256419.html

    其实这里有一个很关键的地方就是kill_fasync异步通知应用程序。这里有很关键的一步,可以说是整个驱动程序的核心:kill_fasync 及 fasync_helper用于异步通知中,其中 kill_fasync(&b_async,SIGIO,POLL_IN)函数的功能是向应用程序发送可读信号,还有那个进程调用fasync_helper函数就向谁发!这个可以结合应用程序是如何拿到信号的对比着看,关于应用程序这里就不说了!网上的资料也比较多讲解的也很详细!例程代码还有理论分析都有!

    fansync_helpr函数内部实现:

    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)  
    2. {  
    3.     struct fasync_struct *fa, **fp;  
    4.     struct fasync_struct *new = NULL;  
    5.     int result = 0;  
    6.   
    7.     if (on) {  
    8.         new = kmem_cache_alloc(fasync_cache, GFP_KERNEL);  
    9.         if (!new)  
    10.             return -ENOMEM;  
    11.     }  
    12.     write_lock_irq(&fasync_lock);  
    13.     for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) {  
    14.         if (fa->fa_file == filp) {  
    15.             if(on) {  
    16.                 fa->fa_fd = fd;   //区分向谁发  
    17.                 kmem_cache_free(fasync_cache, new);  
    18.             } else {  
    19.                 *fp = fa->fa_next;  
    20.                 kmem_cache_free(fasync_cache, fa);  
    21.                 result = 1;  
    22.             }  
    23.             goto out;  
    24.         }  
    25.     }  
    26.   
    27.     if (on) {  
    28.         new->magic = FASYNC_MAGIC;  
    29.         new->fa_file = filp;  
    30.         new->fa_fd = fd;  
    31.         new->fa_next = *fapp;  
    32.         *fapp = new;  
    33.         result = 1;  
    34.     }  
    35. out:  
    36.     write_unlock_irq(&fasync_lock);  
    37.     return result;  
    38. }  
    kill_fasync函数里面的b_async参数:struct fasync_struct类型定义:
    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. struct   fasync_struct   {  
    2.     int magic;  
    3.     int fa_fd;  
    4.     struct fasync_struct *fa_next;    
    5.     struct file   *fa_file;  
    6. };  
    这个参数在下面中也被调用:实现的fasync成员函数
    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. static int gpio_drv_fasync (int fd, struct file *filp, int on)  
    2. {  
    3.     printk("driver: gpio_drv_successful ");  
    4.     return fasync_helper (fd, filp, on, &b_async);  
    5. }  
    这也是应用程序和内核之间传参的一个关键:

    要实现传参,我们需要把一个结构体struct fasync_struct添加到内核的异步队列中,这个结构体用来存放对应设备文件的信息(如fd, filp)并交给内核来管理。一但收到信号,内核就会在这个所谓的异步队列头找到相应的文件(fd),并在filp->owner中找到对应的进程PID,并且调用对应的sig_handler了。

    关于剩下的程序中用到的down() 、up() 还有 DECILARE_MUTEX(b_lock)这里简单的用到了信号量的两个简单的操作,主要是用于保护临界资源,保证中断不被丢失!

    剩下的read和close都比较简单,驱动里面的函数基本都是对应的,close里面一把是释放所有申请的资源!这也是模块化驱动的一个好处!虽然这个驱动很简单!但是要仔细深究起来,里面所涉及的知识量也不小!上面也只是简单的分析总结一下!做个笔记算是对自己的一个提高,也别人在参考的时候能有一点点的帮助!

    最近住的地方没网!感觉好长时间没写博客了!现在业余时间看Linux驱动设备详解,哈哈,比一年多前看的效果好多了,至少书上的好多知识多多少少都接触过!而且看起来还比较有收获,就是看了就忘!看来总结还是相当重要的!好记性不如烂笔头!

  • 相关阅读:
    Binding to a Service
    UML类图几种关系的总结
    阿里云调试
    Serif和Sans-serif字体的区别
    从Log4j迁移到LogBack的理由
    logback
    java 解析json格式数据(转)
    开源Web测试工具介绍
    GET乱码以及POST乱码的解决方法
    单元测试框架TestNg使用总结
  • 原文地址:https://www.cnblogs.com/zzb-Dream-90Time/p/6255714.html
Copyright © 2020-2023  润新知