按键驱动程序(异步通知)

此驱动程序之前的按键驱动程序(中断方式)上加以优化。用到异步通知。对于内核来讲,既然用户想得到的是按键后的状态,那么自然不必时时都要read状态。当它检测到中断发生变主动通知用户,用户再来读。这样,用户空间、内核就可以着手干点其它的事情,而不必忙等按键按下或释放。那么就先从应用程序上面看。

怎么设置相关联到“异步通知”呢?
flag = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flag|FASYNC);
这样的两句就可以将文件描述符fd与异步通知关联,到时候将会接到相关通知。
怎么知道通知道本程序呢?
fcntl(fd, F_SETOWN, getpid());
将其与进程号关联,这样内核就知道要将通知发往本进程。
得到通知后该怎么做呢?
sighandler_t signal(int signum, sighandler_t handler);
后面的handler是接到信号后的处理函数,而对于一般的IO操作,signum通常为SIGIO,这点在内核驱动中要保持一致。handler的定义为
typedef void (*sighandler_t)(int); 在这个函数体内就可以去read数据了。

驱动程序上面又是如何实现的呢?
需要在file_operations结构体里设置.fasync = xxx_fasync,并定义xxx_fasync函数.
static int xxx_fasync(int fd, struct file *filp, int on)
当fcntl(fd, F_SETFL, flag|FASYNC);设置异步通知标志后,就会调用驱动里的xxx_fasync函数,我们需要在驱动程序里先定义一个fasync_struct结构体指针,然后通过xxx_fasync函数再调用int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)去初始化这个结构体。以便在中断服务程序里发送信号。当产生按键中断时,再不是像poll机制那样唤醒等待队列,而是用kill_fasync(struct fasync_struct **fp, int sig, int band)发送信号。应用程序收到信号后会调用自定义的handler做相关处理(本程序是read内核的按键状态)。

详细的驱动程序为:

[plain] view plain copy

print?

  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 <linux/cdev.h>
  9. #include <linux/interrupt.h>
  10. #include <linux/device.h>
  11. #include <linux/sched.h>
  12. #include <linux/gpio.h>
  13. #include <linux/poll.h>
  14. #define FIFTHDEV MKDEV(250, 0)
  15. struct cdev fifthdrv_cdev;
  16. static struct class *fifthdrv_class;
  17. struct button {
  18. int irq;
  19. char *name;
  20. int pin;
  21. int val;
  22. };
  23. //static volatile int pressed = 0;
  24. static unsigned char key_val;
  25. struct fasync_struct *button_async;
  26. //DECLARE_WAIT_QUEUE_HEAD(button_wqh);
  27. /* 六个按键的相关定义整合到结构体 */
  28. static struct button buttons[6] = {
  29. {IRQ_EINT8, "K1", S3C2410_GPG(0), 0x1},
  30. {IRQ_EINT11, "K2", S3C2410_GPG(3), 0x2},
  31. {IRQ_EINT13, "K3", S3C2410_GPG(5), 0x3},
  32. {IRQ_EINT14, "K4", S3C2410_GPG(6), 0x4},
  33. {IRQ_EINT15, "K5", S3C2410_GPG(7), 0x5},
  34. {IRQ_EINT19, "K6", S3C2410_GPG(11),0x6},
  35. };
  36. /* 中断处理函数 */
  37. static irqreturn_t fifthdrv_intr(int irq, void *data)
  38. {
  39. struct button *buttonp;
  40. int val;
  41. buttonp = (struct button*)data;
  42. val = s3c2410_gpio_getpin(buttonp->pin);
  43. if (!val) {/* 按下按键*/
  44. key_val = buttonp->val;
  45. } else { /* 释放按键*/
  46. key_val = buttonp->val | 0x10; //将第4位置1,做标记
  47. }
  48. //pressed = 1; //此处改变按下标志,以使队列不继续睡眠
  49. //wake_up_interruptible(&button_wqh);
  50. kill_fasync(&button_async, SIGIO, POLL_IN);
  51. return IRQ_RETVAL(IRQ_HANDLED);
  52. }
  53. static int fifthdrv_open(struct inode * inode, struct file * file)
  54. {
  55. int i=6;
  56. while(i--){
  57. request_irq(buttons[i].irq, &fifthdrv_intr, IRQ_TYPE_EDGE_BOTH,
  58. buttons[i].name, &buttons[i]);
  59. }
  60. return 0;
  61. }
  62. static ssize_t fifthdrv_read(struct file *file, char __user *user, size_t size,loff_t*o)
  63. {
  64. int sz = sizeof(key_val) ;
  65. if (sz != size) {
  66. return -EINVAL;
  67. }
  68. /* 使用异步通知,此处不必休眠 */
  69. //wait_event_interruptible(button_wqh, pressed);
  70. copy_to_user(user, &key_val, sz);
  71. /* 重新清除按下标志 */
  72. //pressed = 0;
  73. return sz;
  74. }
  75. static int fifthdrv_close(struct inode *inode, struct file *file)
  76. {
  77. int i=6;
  78. while(i--) {
  79. free_irq(buttons[i].irq, &buttons[i]);
  80. }
  81. return 0;
  82. }
  83. /*
  84. static unsigned int fifthdrv_poll(struct file *file, poll_table *wait)
  85. {
  86. unsigned int res=0;
  87. poll_wait(file, &button_wqh, wait);
  88. if (pressed) {
  89. res |= POLLIN | POLLRDNORM;
  90. }
  91. return res;
  92. }
  93. */
  94. static int fifthdrv_fasync(int fd, struct file *filp, int on)
  95. {
  96. return fasync_helper(fd, filp, on, &button_async);
  97. }
  98. static struct file_operations fifthdrv_ops = {
  99. .owner = THIS_MODULE,
  100. .open = fifthdrv_open,
  101. .read = fifthdrv_read,
  102. //.poll = fifthdrv_poll,
  103. .release = fifthdrv_close,
  104. .fasync = fifthdrv_fasync,
  105. };
  106. static int fifthdrv_init(void)
  107. {
  108. int ret;
  109. int devt = FIFTHDEV;
  110. ret = register_chrdev_region(devt, 1, "fifthdrv");
  111. if (ret) {
  112. printk(KERN_ERR "Unable to register minors for fifthdrv\n");
  113. goto fail;
  114. }
  115. fifthdrv_class = class_create(THIS_MODULE, "fifthdrv_class");
  116. if (IS_ERR(fifthdrv_class)) {
  117. printk(KERN_ERR "can‘t register device class\n");
  118. return PTR_ERR(fifthdrv_class);
  119. }
  120. device_create(fifthdrv_class, NULL, devt, NULL, "buttons");
  121. cdev_init(&fifthdrv_cdev, &fifthdrv_ops);
  122. ret = cdev_add(&fifthdrv_cdev, devt, 1);
  123. if (ret < 0)
  124. goto fail_cdev;
  125. return 0;
  126. fail_cdev:
  127. class_unregister(fifthdrv_class);
  128. device_destroy(fifthdrv_class, devt);
  129. cdev_del(&fifthdrv_cdev);
  130. fail:
  131. unregister_chrdev_region(devt, 1);
  132. return 0;
  133. }
  134. static void fifthdrv_exit(void)
  135. {
  136. class_unregister(fifthdrv_class);
  137. device_destroy(fifthdrv_class, FIFTHDEV);
  138. cdev_del(&fifthdrv_cdev);
  139. unregister_chrdev_region(FIFTHDEV, 1);
  140. }
  141. module_init(fifthdrv_init);
  142. module_exit(fifthdrv_exit);
  143. MODULE_LICENSE("GPL");

应用程序为:

  1. #include <stdio.h>
  2. #include <signal.h>
  3. #include <unistd.h>
  4. #include <fcntl.h>
  5. #include <sys/types.h>
  6. int fd;
  7. void signal_f(int signum)
  8. {
  9. unsigned char val;
  10. static unsigned int cnt = 1;
  11. read(fd, &val, sizeof(val));
  12. printf("cnt:%d, val:0x%x\n", cnt++, val);
  13. }
  14. int main(int argc, char *argv[])
  15. {
  16. int flag;
  17. fd = open("/dev/buttons", O_RDWR);
  18. if (fd < 0)
  19. {
  20. printf("can‘t open!\n");
  21. }
  22. signal(SIGIO, signal_f);
  23. fcntl(fd, F_SETOWN, getpid());
  24. flag = fcntl(fd, F_GETFL);
  25. fcntl(fd, F_SETFL, flag|FASYNC);
  26. while (1)
  27. {
  28. sleep(1000);
  29. }
  30. return 0;
  31. }

运行状态:

本文转自:http://blog.chinaunix.NET/uid-22609852-id-3153120.html

时间: 2024-10-13 15:59:17

按键驱动程序(异步通知)的相关文章

按键驱动异步通知

在此以前,我们都是让应用程序主动去读按键的状态,有没有一种情况,当驱动程序有数据时,主动去告诉应用程序,告诉它,有数据了,你赶紧来读吧.这种情况在linux里的专业术语就叫异步通知. 在按键的例子中异步通知可以理解为:当按键按下时,驱动程序会提醒(即触发)应用程序(通过信号signal来实现). 举一个例子:进程之间发信号 原来我们常用  kill 这个命令 : kill       -9    pid   kill这个命令就是一个发信号 发送者  :   kill 接收者  :   pid 信

关于 exynos 4412 按键中断 异步通知

以下是驱动测试代码: //内核的驱动代码 #include <linux/init.h> #include <linux/module.h> //for module_init moudule_exit #include <linux/fs.h> //for MKDEV register_chrdev_region #include <linux/cdev.h> //字符设备头文件 #include <linux/device.h> #inclu

入门级的按键驱动——按键驱动笔记之poll机制-异步通知-同步互斥阻塞-定时器防抖

文章对应视频的第12课,第5.6.7.8节. 在这之前还有查询方式的驱动编写,中断方式的驱动编写,这篇文章中暂时没有这些类容.但这篇文章是以这些为基础写的,前面的内容有空补上. 按键驱动——按下按键,打印键值: 目录 概要 poll机制 异步通知 同步互斥阻塞 定时器防抖 概要: 查询方式: 12-3 缺点:占用CPU99%的资源.中断方式:12-4 缺点:调用read函数后如果没有按键按下,该函数永远不会结束,一直在等待按键按下. 优点:使用到了休眠机制,占用cpu资源极少.poll机制: 1

字符设备驱动程序之异步通知(韦大仙)

读取按键的方法: (1)查询的方式:极度耗费资源 (2)中断的方式:如果没有按键按下,read函数会一直的等待 (3)poll机制的引入:可以指定超时时间 上述三种方式有一个共同点:应用程序主动的去查询. 问题:有没有一种方式当有按键按下时,驱动程序通知应用程序去读取.这就是本节所说的异步通知,该方式用信号的方式来实现的. 进程间发信号,例如: kill  -9  PID kill这个程序是发送者,进程号为PID的进程为接受者,9就是发送的信号.接下来引入signal函数: signal 函数是

9.按键之使用异步通知

之前学的应用层都是: 1)查询方式:一直读 2)中断方式.同样一直读,直到中断进程唤醒 3)poll机制:一直在poll函数中睡眠,一定时间读一次 以上3种,我们都是让应用程序主动去读,本节我们学习异步通知,它的作用就是当驱动层有数据时,主动告诉应用程序,然后应用程序再来读, 这样,应用程序就可以干其它的事情,不必一直读 比如:kill -9 pid ,其实就是通过发信号杀死进程,kill发数据9给指定id号进程 1.怎么来收信号? 通过signal函数来实现获取信号,先来看看以下例子: 头函数

字符设备驱动程序之异步通知

异步通知: 驱动程序的所谓异步通知,就是说并不是应用程序来对驱动程序操作的,而是驱动程序查询到有事件发生或者有数据发生变化的时候通知应用程序.角色发生了变化,应用程序由主动改为被动执行. 比如按键驱动: 1.有不断进行查询引脚状态的,CPU资源消耗非常的打: 2.有中断操作的,发生按键事件后采取执行相关事件处理函数,需要应用程序不断执行read函数,使得不能去干其它事情: 3.poll机制,改善了中断方式操作,在应用程序上当没有事件发生时,会跳去read函数继续执行其它的任务,知道有事件发生才返

linux驱动的异步通知(kill_fasync,fasync)---- 驱动程序向应用程序发送信号

应用程序 [cpp] view plain copy #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <poll.h> #include <signal.h> #include <sys/types.h> #include <unistd.h> #include <fcn

linux设备驱动程序中的阻塞、IO多路复用与异步通知机制

一.阻塞与非阻塞 阻塞与非阻塞是设备访问的两种方式.在写阻塞与非阻塞的驱动程序时,经常用到等待队列. 阻塞调用是指调用结果返回之前,当前线程会被挂起,函数只有在得到结果之后才会返回. 非阻塞指不能立刻得到结果之前,该函数不会阻塞当前进程,而会立刻返回. 函数是否处于阻塞模式和驱动对应函数中的实现机制是直接相关的,但并不是一一对应的,例如我们在应用层设置为阻塞模式,如果驱动中没有实现阻塞,函数仍然没有阻塞功能. 二.等待队列 在linux设备驱动程序中,阻塞进程可以使用等待队列来实现. 在内核中,

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

目录 按键驱动方式对比 进程间发信号 目标 如何让驱动通知应用 程序编写 驱动程序 应用程序 完整代码如下 测试 title: 字符设备驱动(七)按键异步通知 tags: linux date: 2018-11-24 16:39:47 toc: true --- 按键驱动方式对比 查询:耗资源 中断: 没有超时机制,当没有中断作为生产者,read函数一直休眠 poll机制,加入超时机制 上述三种都是app主动去获取按键,使用异步通知的形式可以使按键发生后,通知app去读取 进程间发信号 以前使用