Chinaunix首页 | 论坛 | 博客
  • 博客访问: 829162
  • 博文数量: 281
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 2770
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-02 19:45
个人简介

邮箱:zhuimengcanyang@163.com 痴爱嵌入式技术的蜗牛

文章分类
文章存档

2020年(1)

2018年(1)

2017年(56)

2016年(72)

2015年(151)

分类: 嵌入式

2015-08-18 14:43:39

目的:

前面讲了3中读取按键的方式
(1)查询方式: CPU一直在查询按键的状态值。
(2)中断方式: 按键按下触发中断,返回按键值;如果按键没按下,则read函数将进入休眠,一直不返回。---> 阻塞的方式
(3)poll机制 +中断方式: 在(2)的基础上加入poll机制,设定了一个超时时间。即如果没有按键被按下,但是到了超时时间,也需要返回。--> 非阻塞方式
这三种方式,都需要由应用程序不断的去查询是不是有按键被按下(在POLL机制中,是在超时时间里面进行查询按键值)。
有没有一种方式,是按键被按下的时候,驱动主动发送一个通知到应用程序,表示它按下了,赶紧过来处理了。这有点类似于“中断”的工作方式,而不需要在应用程序里一直查询这个按键的状态。当然有,这就是异步通知。
点击:异步通知原理

本节目标:按下按键时,驱动主动去通知应用程序。

问:如何实现异步通知,有哪些要素?

答:有四个要素:

一、应用程序要实现有:注册信号处理函数,使用signal函数

二、谁来发?驱动来发

三、发给谁?发给应用程序,但应用程序必须告诉驱动PID

四、怎么发?驱动程序使用kill_fasync函数

问:应该在驱动的哪里调用kill_fasync函数?

答:kill_fasync函数的作用是,当有数据时去通知应用程序,理所当然的应该在用户终端处理函数里调用。

问:file_operations需要添加什么函数指针成员吗?

答:要的,需要添加fasync函数指针,要实现这个函数指针,幸运的是,这个函数仅仅调用了fasync_helper函数,而且这个函数是内核帮我们实现好了,驱动工程师不用修改,fasync_helper函数的作用是初始化/释放fasync_struct







1. 硬件连接图
2. 驱动程序

点击(此处)折叠或打开

  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. static struct class *fifthdrv_class;
  14. static struct class_device    *fifthdrv_class_dev;

  15. volatile unsigned long *gpfcon;
  16. volatile unsigned long *gpfdat;

  17. volatile unsigned long *gpgcon;
  18. volatile unsigned long *gpgdat;


  19. static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

  20. /* 中断事件标志, 中断服务程序将它置1,fifth_drv_read将它清0 */
  21. static volatile int ev_press = 0;

  22. static struct fasync_struct *button_async;


  23. struct pin_desc{
  24.     unsigned int pin;
  25.     unsigned int key_val;
  26. };


  27. /* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
  28. /* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
  29. static unsigned char key_val;

  30. struct pin_desc pins_desc[4] = {
  31.     {S3C2410_GPF0, 0x01},
  32.     {S3C2410_GPF2, 0x02},
  33.     {S3C2410_GPG3, 0x03},
  34.     {S3C2410_GPG11, 0x04},
  35. };


  36. /*
  37.   * 确定按键值
  38.   */
  39. static irqreturn_t buttons_irq(int irq, void *dev_id)
  40. {
  41.     struct pin_desc * pindesc = (struct pin_desc *)dev_id;
  42.     unsigned int pinval;
  43.     
  44.     pinval = s3c2410_gpio_getpin(pindesc->pin);

  45.     if (pinval)
  46.     {
  47.         /* 松开 */
  48.         key_val = 0x80 | pindesc->key_val;
  49.     }
  50.     else
  51.     {
  52.         /* 按下 */
  53.         key_val = pindesc->key_val;
  54.     }

  55.     ev_press = 1; /* 表示中断发生了 */
  56.     wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */
  57.    
  58.     // 在中断里面,发送SIGIO信号,通知应用程序。
  59.     kill_fasync (&button_async, SIGIO, POLL_IN);
  60.     
  61.     return IRQ_RETVAL(IRQ_HANDLED);
  62. }

  63. static int fifth_drv_open(struct inode *inode, struct file *file)
  64. {
  65.     /* 配置GPF0,2为输入引脚 */
  66.     /* 配置GPG3,11为输入引脚 */
  67.     request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);
  68.     request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);
  69.     request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]);
  70.     request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]);    

  71.     return 0;
  72. }

  73. ssize_t fifth_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
  74. {
  75.     if (size != 1)
  76.         return -EINVAL;

  77.     /* 如果没有按键动作, 休眠 */
  78.     wait_event_interruptible(button_waitq, ev_press);

  79.     /* 如果有按键动作, 返回键值 */
  80.     copy_to_user(buf, &key_val, 1);
  81.     ev_press = 0;
  82.     
  83.     return 1;
  84. }


  85. int fifth_drv_close(struct inode *inode, struct file *file)
  86. {
  87.     free_irq(IRQ_EINT0, &pins_desc[0]);
  88.     free_irq(IRQ_EINT2, &pins_desc[1]);
  89.     free_irq(IRQ_EINT11, &pins_desc[2]);
  90.     free_irq(IRQ_EINT19, &pins_desc[3]);
  91.     return 0;
  92. }

  93. static unsigned fifth_drv_poll(struct file *file, poll_table *wait)
  94. {
  95.     unsigned int mask = 0;
  96.     poll_wait(file, &button_waitq, wait); // 不会立即休眠

  97.     if (ev_press)
  98.         mask |= POLLIN | POLLRDNORM;

  99.     return mask;
  100. }


  101. // 这个函数在什么时候被调用呢????     必须要自己定义的函数。
  102. static int fifth_drv_fasync (int fd, struct file *filp, int on)
  103. {
  104.     printk("driver: fifth_drv_fasync\n");
  105.     return fasync_helper (fd, filp, on, &button_async);
  106. }


  107. static struct file_operations sencod_drv_fops = {
  108.     .owner   = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
  109.     .open    = fifth_drv_open,
  110.     .read    = fifth_drv_read,    
  111.     .release = fifth_drv_close,
  112.     .poll    = fifth_drv_poll,
  113.     .fasync  = fifth_drv_fasync,
  114. };


  115. int major;
  116. static int fifth_drv_init(void)
  117. {
  118.     major = register_chrdev(0, "fifth_drv", &sencod_drv_fops);

  119.     fifthdrv_class = class_create(THIS_MODULE, "fifth_drv");
  120.     fifthdrv_class_dev = class_device_create(fifthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */

  121.     gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
  122.     gpfdat = gpfcon + 1;

  123.     gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);
  124.     gpgdat = gpgcon + 1;

  125.     return 0;
  126. }

  127. static void fifth_drv_exit(void)
  128. {
  129.     unregister_chrdev(major, "fifth_drv");
  130.     class_device_unregister(fifthdrv_class_dev);
  131.     class_destroy(fifthdrv_class);
  132.     iounmap(gpfcon);
  133.     iounmap(gpgcon);
  134.     return 0;
  135. }


  136. module_init(fifth_drv_init);
  137. module_exit(fifth_drv_exit);
  138. MODULE_LICENSE("GPL");


3. 测试程序

点击(此处)折叠或打开

  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4. #include <stdio.h>
  5. #include <poll.h>
  6. #include <signal.h>
  7. #include <sys/types.h>
  8. #include <unistd.h>
  9. #include <fcntl.h>


  10. /* fifthdrvtest
  11.   */
  12. int fd;

  13. void my_signal_fun(int signum)
  14. {
  15.     unsigned char key_val;
  16.     read(fd, &key_val, 1);
  17.     printf("key_val: 0x%x\n", key_val);
  18. }

  19. int main(int argc, char **argv)
  20. {
  21.     unsigned char key_val;
  22.     int ret;
  23.     int Oflags;

  24.     /**  
  25.        void (*signal(int signum,void(* handler)(int)))(int);
        *  第一个参数signum指明了所要处理的信号类型
        *  第二个参数handler描述了与信号关联的动作
        **/
  26.     signal(SIGIO, my_signal_fun);    // 驱动程序里面,当有按键按下时,发送一个SIGIO信号,应用程序调用回调函数my_signal_fun
  27.     
  28.     fd = open("/dev/buttons", O_RDWR);
  29.     if (fd < 0)
  30.     {
  31.         printf("can't open!\n");
  32.     }

  33.     fcntl(fd, F_SETOWN, getpid())//指定当前进程作为文件fd(设备文件)的owner,目的是让内核(内核操作设备文件)知道应该通知那个进程
  34.     Oflags = fcntl(fd, F_GETFL);          //获取标志位
  35.     fcntl(fd, F_SETFL, Oflags | FASYNC)//启用异步通知机制


  36.     while (1)
  37.     {
  38.         sleep(1000);
  39.     }
  40.     
  41.     return 0;
  42. }

表头文件#include
功 能:设置某一信号的对应动作
函数原型:void (*signal(int signum,void(* handler)(int)))(int);
或者:typedef void (*sig_t)( int );
sig_t signal(int signum,sig_t handler);
参数说明
第一个参数signum指明了所要处理的信号类型,它可以取除了SIGKILL和SIGSTOP外的任何一种信号。
第二个参数handler描述了与信号关联的动作,它可以取以下三种值:
(1)一个无返回值的函数地址
此函数必须在signal()被调用前申明,handler中为这个函数的名字。当接收到一个类型为signum的信号时,就执行handler 所指定的函数。这个函数应有如下形式的定义:
void func(int sig);
(2)SIG_IGN
这个符号表示忽略该信号,执行了相应的signal()调用后,进程会忽略类型为sig的信号。
(3)SIG_DFL
这个符号表示恢复系统对信号的默认处理。
函数说明
signal()会依参数signum 指定的信号编号来设置该信号的处理函数。当指定的信号到达时就会跳转到参数handler指定的函数执行。当一个信号的信号处理函数执行时,如果进程又接 收到了该信号,该信号会自动被储存而不会中断信号处理函数的执行,直到信号处理函数执行完毕再重新调用相应的处理函数。但是如果在信号处理函数执行时进程 收到了其它类型的信号,该函数的执行就会被中断。
返回值:返回先前的信号处理,如果有错误则返回SIG_ERR(-1)。
下面的情况可以产生Signal:
  1. 按下CTRL+C产生SIGINT
  2. ,如除0,非法内存访问(SIGSEV)等等
  3. Kill函数可以对进程发送Signal
  4. Kill命令。实际上是对Kill函数的一个包装
  5. 软件中断。如当Alarm Clock超时(SIGURG),当Reader中止之后又向管道写数据(SIGPIPE),等等


4. 测试

5. 小结
异步通知的方式:从驱动发送信号到应用层。和前面的实现方式都不同。

(1)目标:
     按下按键,驱动程序通知应用程序。

(2)涉及的知识点:
(1)应用程序: 注册信号处理函数
(2)谁发新号: 驱动
(3)发给谁:应用程序(驱动程序<-->应用程序PID)
(4)怎么发? kill_fasyn()函数(源码里面找)

(3)具体实现:
为了使设备支持异步通知机制,驱动程序中涉及以下3项工作:
1. 支持F_SETOWN命令,能在这个控制命令处理中设置filp->f_owner为对应进程ID。
   不过此项工作已由内核完成,设备驱动无须处理。
2. 支持F_SETFL命令的处理,每当FASYNC标志改变时,驱动程序中的fasync()函数将得以执行(驱动中构造fasync()函数函数的原因)。
   驱动中应该实现fasync()函数。
3. 在设备资源可获得时,调用kill_fasync()函数激发相应的信号

应用程序:
fcntl(fd, F_SETOWN, getpid());  // 告诉内核,发给谁
Oflags = fcntl(fd, F_GETFL);   
fcntl(fd, F_SETFL, Oflags | FASYNC);  // 改变fasync标记,最终会调用到驱动的faync > fasync_helper:初始化/释放fasync_struct







阅读(660) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~