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

版权声明:本文为博主原创文章,未经博主允许不得转载。

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

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

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

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

下面是驱动的源码:

  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. *中断事件标志,中断服务程序将它置1,在gpio_drv_read将它清0
  37. */
  38. static volatile int ev_press = 0;
  39. /*
  40. *异步结构体指针 用于读
  41. */
  42. static struct fasync_struct *b_async;
  43. /*
  44. *中断引脚描述结构体
  45. */
  46. struct pin_desc_s{
  47. unsigned int pin;
  48. unsigned int key_val;
  49. unsigned int irq;
  50. };
  51. static unsigned char key_val;
  52. struct pin_desc_s pin_desc[5] = {
  53. {MXS_PIN_TO_GPIO(PINID_LCD_ENABLE),0x03,},    /* IO1 rain GPIO1_31     */
  54. {MXS_PIN_TO_GPIO(PINID_LCD_HSYNC),0x05,},     /* IO2 windspeed GPIO1_29*/
  55. {MXS_PIN_TO_GPIO(PINID_LCD_DOTCK),0x0A,},     /* 机箱门             */
  56. {MXS_PIN_TO_GPIO(PINID_AUART3_RX),0x07,},     /* key1 GPIO3_12         */
  57. {MXS_PIN_TO_GPIO(PINID_AUART3_TX),0x09,},     /* key2 GPIO3_13         */
  58. };
  59. static DECLARE_MUTEX(b_lock);
  60. static DECLARE_WAIT_QUEUE_HEAD(b_waitq);
  61. static irqreturn_t b_irq(int irq, void *dev_id)
  62. {
  63. struct pin_desc_s * pindesc = (struct pin_desc_s *)dev_id;
  64. unsigned int pinval;
  65. pinval = gpio_get_value(pindesc->pin);
  66. if (pinval)
  67. {
  68. key_val = 1;
  69. }
  70. else
  71. {
  72. key_val = pindesc->key_val;
  73. }
  74. ev_press = 1;
  75. wake_up_interruptible(&b_waitq);        //唤醒等待队列里面的进程
  76. kill_fasync(&b_async, SIGIO, POLL_IN);  //异步通知
  77. //printk("interrupt occur..........\n");
  78. return IRQ_RETVAL(IRQ_HANDLED);
  79. }
  80. static int gpio_drv_open(struct inode *inode, struct file *file)
  81. {
  82. int iRet[5]={0};
  83. int i = 0;
  84. if (file->f_flags & O_NONBLOCK)
  85. {
  86. if (down_trylock(&b_lock))
  87. return -EBUSY;
  88. }
  89. else
  90. {
  91. down(&b_lock);
  92. }
  93. for(i = 0; i < 5; i++)
  94. {
  95. gpio_direction_input((pin_desc[i]).pin);
  96. (pin_desc[i]).irq = gpio_to_irq((pin_desc[i]).pin);
  97. if ((pin_desc[i]).irq)
  98. disable_irq((pin_desc[i]).irq);
  99. set_irq_type((pin_desc[i]).irq, IRQF_TRIGGER_FALLING);  //下降沿中断
  100. iRet[i] = request_irq((pin_desc[i]).irq, buttons_irq, IRQF_SHARED, "gpio_int", &pin_desc[i]);
  101. if (iRet[i] != 0){
  102. printk("request irq failed!! ret: %d  irq:%d \n", iRet[i],(pin_desc[i]).irq);
  103. return -EBUSY;}
  104. }
  105. return 0;
  106. }
  107. ssize_t gpio_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
  108. {
  109. if (size != 1)
  110. return -EINVAL;
  111. if (file->f_flags & O_NONBLOCK)
  112. {
  113. if (!ev_press)
  114. return -EAGAIN;
  115. }
  116. else
  117. {
  118. wait_event_interruptible(b_waitq, ev_press);
  119. }
  120. copy_to_user(buf, &key_val, 1);
  121. ev_press = 0;
  122. return 1;
  123. }
  124. int gpio_drv_close(struct inode *inode, struct file *file)
  125. {
  126. int i = 0;
  127. for( i = 0; i < 5; i++)
  128. {
  129. free_irq((pin_desc[i]).irq, &pin_desc[i]);
  130. }
  131. up(&b_lock);
  132. return 0;
  133. }
  134. static int gpio_drv_fasync (int fd, struct file *filp, int on)
  135. {
  136. printk("driver: gpio_drv_successful\n");
  137. return fasync_helper (fd, filp, on, &b_async);
  138. }
  139. static struct file_operations gpio_drv_fops = {
  140. .owner      = THIS_MODULE,
  141. .open       = gpio_drv_open,
  142. .read       = gpio_drv_read,
  143. .release    = gpio_drv_close,
  144. .fasync     = gpio_drv_fasync,
  145. };
  146. static struct miscdevice b_miscdev =
  147. {
  148. .minor          = MISC_DYNAMIC_MINOR,
  149. .name           = "magic-gpio",
  150. .fops           = &gpio_drv_fops,
  151. };
  152. static int __init gpio_drv_init(void)
  153. {
  154. int iRet=0;
  155. printk("gpio_miscdev module init!\n");
  156. iRet = misc_register(&b_miscdev);
  157. if (iRet) {
  158. printk("register failed!\n");
  159. }
  160. return 0;
  161. }
  162. static void __exit gpio_drv_exit(void)
  163. {
  164. printk("gpio_miscdev module exit!\n");
  165. misc_deregister(&b_miscdev);
  166. }
  167. module_init(gpio_drv_init);
  168. module_exit(gpio_drv_exit);
  169. MODULE_AUTHOR("HEHAI & RK");
  170. MODULE_LICENSE("Dual BSD/GPL");
  171. MODULE_DESCRIPTION("gpio interrupt module");

首先还是先从init函数来总结:该驱动是一混杂设备驱动模型来写的,这个主要是借鉴网上的好多资料都是一这种模式来写的,Linux里面misc混杂设备驱动的主设备号是为10的驱动设备,init模块首先是用 misc_register()函数注册一个一个混杂设备驱动,参数一个混杂设备驱动里面非常重要的一个数据结构 struct miscdevice,下面把原型贴出来:

  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. };

当然我上面的驱动代码只初始化了前面的关键三项:

  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函数里面了!

  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函数:

  1. static int gpio_drv_open(struct inode *inode, struct file *file)
  2. {
  3. int iRet[5]={0};
  4. int i = 0;
  5. if (file->f_flags & O_NONBLOCK)//非阻塞
  6. {
  7. if (down_trylock(&b_lock))
  8. return -EBUSY;
  9. }
  10. else
  11. {
  12. down(&b_lock);
  13. }
  14. for(i = 0; i < 5; i++)
  15. {
  16. gpio_direction_input((pin_desc[i]).pin);//设置对应的GPIO输入
  17. (pin_desc[i]).irq = gpio_to_irq((pin_desc[i]).pin);//把GPIO对应的pin值转换为相应的IRQ值并返回
  18. if ((pin_desc[i]).irq)
  19. disable_irq((pin_desc[i]).irq);//先关闭中断并等待中断处理完
  20. set_irq_type((pin_desc[i]).irq, IRQF_TRIGGER_FALLING);  //设置下降沿中断
  21. iRet[i] = request_irq((pin_desc[i]).irq, b_irq, IRQF_SHARED, "gpio_int", &pin_desc[i]);
  22. if (iRet[i] != 0){
  23. printk("request irq failed!! ret: %d  irq:%d \n", iRet[i],(pin_desc[i]).irq);
  24. return -EBUSY;}
  25. }
  26. return 0;
  27. }

这里可以详细了解一下关于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函数:

  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. pinval = gpio_get_value(pindesc->pin);
  6. if (pinval)
  7. {
  8. key_val = 1;
  9. }
  10. else
  11. {
  12. key_val = pindesc->key_val;
  13. }
  14. ev_press = 1;
  15. wake_up_interruptible(&b_waitq);        //唤醒等待队列里面的进程
  16. kill_fasync(&b_async, SIGIO, POLL_IN);  //异步通知
  17. //printk("interrupt occur..........\n");
  18. return IRQ_RETVAL(IRQ_HANDLED);
  19. }

其中上面的b_waitq是这样定义的:

  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函数内部实现:

  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. if (on) {
  7. new = kmem_cache_alloc(fasync_cache, GFP_KERNEL);
  8. if (!new)
  9. return -ENOMEM;
  10. }
  11. write_lock_irq(&fasync_lock);
  12. for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) {
  13. if (fa->fa_file == filp) {
  14. if(on) {
  15. fa->fa_fd = fd;   //区分向谁发
  16. kmem_cache_free(fasync_cache, new);
  17. } else {
  18. *fp = fa->fa_next;
  19. kmem_cache_free(fasync_cache, fa);
  20. result = 1;
  21. }
  22. goto out;
  23. }
  24. }
  25. if (on) {
  26. new->magic = FASYNC_MAGIC;
  27. new->fa_file = filp;
  28. new->fa_fd = fd;
  29. new->fa_next = *fapp;
  30. *fapp = new;
  31. result = 1;
  32. }
  33. out:
  34. write_unlock_irq(&fasync_lock);
  35. return result;
  36. }

kill_fasync函数里面的b_async参数:struct fasync_struct类型定义:

  1. struct   fasync_struct   {
  2. int magic;
  3. int fa_fd;
  4. struct fasync_struct *fa_next;
  5. struct file   *fa_file;
  6. };

这个参数在下面中也被调用:实现的fasync成员函数

  1. static int gpio_drv_fasync (int fd, struct file *filp, int on)
  2. {
  3. printk("driver: gpio_drv_successful\n");
  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驱动设备详解,哈哈,比一年多前看的效果好多了,至少书上的好多知识多多少少都接触过!而且看起来还比较有收获,就是看了就忘!看来总结还是相当重要的!好记性不如烂笔头!

Linux的fasync驱动异步通知详解【转】的更多相关文章

  1. 迅为4412开发板Linux驱动教程——总线_设备_驱动注册流程详解

    本文转自:http://www.topeetboard.com 视频下载地址: 驱动注册:http://pan.baidu.com/s/1i34HcDB 设备注册:http://pan.baidu.c ...

  2. Linux 进程间通信 --- 信号通信 --- signal --- signal(SIGINT, my_func); --- 按键驱动异步通知(转)

    信号  ( signal ) 机制是 UNIX 系统中最为古老的进程间通信机制,很多条件可以产生一个信号. 信号的产生: 1,当用户按下某些按键时,产生信号. 2,硬件异常产生信号:除数为 0 ,无效 ...

  3. Linux内核开发之异步通知与异步I/O(一)

    “小王,听说过锦上添花吧..”我拍拍下王的头说. “还锦上添花你,为你上次提的几个东东,我是头上长包..”小王气愤地瞪着我. “啊,为啥这样呢,本来还特意拒绝了MM的月份,抽出时间打算给你说点高级的东 ...

  4. linux mount命令参数及用法详解

    linux mount命令参数及用法详解 非原创,主要来自 http://www.360doc.com/content/13/0608/14/12600778_291501907.shtml. htt ...

  5. Linux中/proc目录下文件详解

    转载于:http://blog.chinaunix.net/uid-10449864-id-2956854.html Linux中/proc目录下文件详解(一)/proc文件系统下的多种文件提供的系统 ...

  6. linux dmesg命令参数及用法详解(linux显示开机信息命令)

    linux dmesg命令参数及用法详解(linux显示开机信息命令) http://blog.csdn.net/zhongyhc/article/details/8909905 功能说明:显示开机信 ...

  7. 莱特币ltc在linux下的多种挖矿方案详解

    莱特币ltc在linux下的多种挖矿方案详解 4.0.1 Nvidia显卡Linux驱动Nvidia全部驱动:http://www.nvidia.cn/Download/index.aspx?lang ...

  8. Linux C 语言之 Hello World 详解

    目录 Linux C 语言之 Hello World 详解 第一个 C 语言程序 程序运行原理 编译,链接 运行时 链接库 编译器优化 Hello World 打印原理 stdout, stdin 和 ...

  9. Linux中/proc目录下文件详解(转贴)

      转载:http://www.sudu.cn/info/index.php?op=article&id=302529   Linux中/proc目录下文件详解(一) 声明:可以自由转载本文, ...

随机推荐

  1. 为Office365增加密码过期自动提醒功能

    最近有人和我反应,无法查看所有员工的Office365的密码过期时间.最好可以批量查看,如果能在过期前几天发个提醒邮件就更好了. $pw = ConvertTo-SecureString 'your_ ...

  2. UE4开发神秘海域类游戏原型 初阶(二):动画资源的整合

    前一篇已经确定神海类游戏原型的目标,首先要做的就是3C's(Character, Controls, Camera)的开发.   UE4的3C's的程序部分开发主要也就是基于他的GamePlay Fr ...

  3. mysql关于排序值的问题

  4. 如何让tomcat不记录catalina.out这个日志文件

    tomcat日志记录配置在conf/logging.properties中 有这5类日志 catalina,localhost,manager,admin(控制台),host-manager 还有8个 ...

  5. C#异常处理性能测试

    异常处理性能测试 using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq ...

  6. 监控redis python脚本

    #!/bin/env python #-*- coding:utf- -*- import json import time import socket import os import re imp ...

  7. ue4 ios

    project settings package 可以指定非APK打包,确定资源是否发布 可以指定content下某个目录所有文件(非.uassert)都打包 ios环境下fopen打开文件需要指定路 ...

  8. 配置Openfire的eclipse项目

    官方文档在这里 Install JDK Download JDK and install them. The least version should be 1.5. I use 1.6. Sorry ...

  9. XML和JSON数据格式对比

    概念 XML 扩展标记语言 (Extensible Markup Language, XML) ,用于标记电子文件使其具有结构性的标记语言,可以用来标记数据.定义数据类型,是一种允许用户对自己的标记语 ...

  10. zabbix利用自带的模板监控mysql数据库

    zabbix利用自带的模板监控mysql数据库 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 有些东西你不会的时候觉得它特别难,但是当你去做的时候就发现如此的简单~zabbix功能 ...