在上一节中,我们讲解了如何自动创建设备节点,实现一个中断方式的按键驱动。虽然中断式的驱动,效率是蛮高的,但是大家有没有发现,应用程序的死循环里的读函数是一直在读的;在实际的应用场所里,有没有那么一种情况,偶尔有数据、偶尔没有数据,答案当然是有的。我们理想当然的就会想到,当有数据的时候,我们才去读它,没数据的时候我们读它干啥?岂不浪费劳动力?
上一节文章链接:http://blog.csdn.net/lwj103862095/article/details/17511867
这一节里,我们在中断的基础上添加poll机制来实现有数据的时候就去读,没数据的时候,自己规定一个时间,如果还没有数据,就表示超时时间。

poll机制总结:(韦老师总结的,不是我总结的哦!)

1. poll > sys_poll > do_sys_poll >poll_initwait,poll_initwait函数注册一下回调函数__pollwait,它就是我们的驱动程序执行poll_wait时,真正被调用的函数。

2. 接下来执行file->f_op->poll,即我们驱动程序里自己实现的poll函数

它会调用poll_wait把自己挂入某个队列,这个队列也是我们的驱动自己定义的;

它还判断一下设备是否就绪。

3. 如果设备未就绪,do_sys_poll里会让进程休眠一定时间

4. 进程被唤醒的条件有2:一是上面说的“一定时间”到了,二是被驱动程序唤醒。驱动程序发现条件就绪时,就把“某个队列”上挂着的进程唤醒,这个队列,就是前面通过poll_wait把本进程挂过去的队列。

5. 如果驱动程序没有去唤醒进程,那么chedule_timeout(__timeou)超时后,会重复2、3动作,直到应用程序的poll调用传入的时间到达。

问:这一节与上一节的在驱动里添加了哪些内容?

答:仅仅添加了poll函数,该函数如下:

  1. static unsigned int fourth_drv_poll(struct file *file, poll_table *wait)
  2. {
  3. unsigned int mask = 0;
  4. /* 该函数,只是将进程挂在button_waitq队列上,而不是立即休眠 */
  5. poll_wait(file, &button_waitq, wait);
  6. /* 当没有按键按下时,即不会进入按键中断处理函数,此时ev_press = 0
  7. * 当按键按下时,就会进入按键中断处理函数,此时ev_press被设置为1
  8. */
  9. if(ev_press)
  10. {
  11. mask |= POLLIN | POLLRDNORM;  /* 表示有数据可读 */
  12. }
  13. /* 如果有按键按下时,mask |= POLLIN | POLLRDNORM,否则mask = 0 */
  14. return mask;
  15. }

详细请参考驱动源码:

  1. #include <linux/kernel.h>
  2. #include <linux/fs.h>
  3. #include <linux/init.h>
  4. #include <linux/delay.h>
  5. #include <linux/irq.h>
  6. #include <asm/uaccess.h>
  7. #include <asm/irq.h>
  8. #include <asm/io.h>
  9. #include <linux/module.h>
  10. #include <linux/device.h>         //class_create
  11. #include <mach/regs-gpio.h>       //S3C2410_GPF1
  12. //#include <asm/arch/regs-gpio.h>
  13. #include <mach/hardware.h>
  14. //#include <asm/hardware.h>
  15. #include <linux/interrupt.h>  //wait_event_interruptible
  16. #include <linux/poll.h>   //poll
  17. /* 定义并初始化等待队列头 */
  18. static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
  19. static struct class *fourthdrv_class;
  20. static struct device *fourthdrv_device;
  21. static struct pin_desc{
  22. unsigned int pin;
  23. unsigned int key_val;
  24. };
  25. static struct pin_desc pins_desc[4] = {
  26. {S3C2410_GPF1,0x01},
  27. {S3C2410_GPF4,0x02},
  28. {S3C2410_GPF2,0x03},
  29. {S3C2410_GPF0,0x04},
  30. };
  31. static int ev_press = 0;
  32. /* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
  33. /* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
  34. static unsigned char key_val;
  35. int major;
  36. /* 用户中断处理函数 */
  37. static irqreturn_t buttons_irq(int irq, void *dev_id)
  38. {
  39. struct pin_desc *pindesc = (struct pin_desc *)dev_id;
  40. unsigned int pinval;
  41. pinval = s3c2410_gpio_getpin(pindesc->pin);
  42. if(pinval)
  43. {
  44. /* 松开 */
  45. key_val = 0x80 | (pindesc->key_val);
  46. }
  47. else
  48. {
  49. /* 按下 */
  50. key_val = pindesc->key_val;
  51. }
  52. ev_press = 1;                            /* 表示中断已经发生 */
  53. wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */
  54. return IRQ_HANDLED;
  55. }
  56. static int fourth_drv_open(struct inode * inode, struct file * filp)
  57. {
  58. /*  K1 ---- EINT1,K2 ---- EINT4,K3 ---- EINT2,K4 ---- EINT0
  59. *  配置GPF1、GPF4、GPF2、GPF0为相应的外部中断引脚
  60. *  IRQT_BOTHEDGE应该改为IRQ_TYPE_EDGE_BOTH
  61. */
  62. request_irq(IRQ_EINT1, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K1",&pins_desc[0]);
  63. request_irq(IRQ_EINT4, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K2",&pins_desc[1]);
  64. request_irq(IRQ_EINT2, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K3",&pins_desc[2]);
  65. request_irq(IRQ_EINT0, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K4",&pins_desc[3]);
  66. return 0;
  67. }
  68. static ssize_t fourth_drv_read(struct file *file, char __user *user, size_t size,loff_t *ppos)
  69. {
  70. if (size != 1)
  71. return -EINVAL;
  72. /* 当没有按键按下时,休眠。
  73. * 即ev_press = 0;
  74. * 当有按键按下时,发生中断,在中断处理函数会唤醒
  75. * 即ev_press = 1;
  76. * 唤醒后,接着继续将数据通过copy_to_user函数传递给应用程序
  77. */
  78. wait_event_interruptible(button_waitq, ev_press);
  79. copy_to_user(user, &key_val, 1);
  80. /* 将ev_press清零 */
  81. ev_press = 0;
  82. return 1;
  83. }
  84. static int fourth_drv_close(struct inode *inode, struct file *file)
  85. {
  86. free_irq(IRQ_EINT1,&pins_desc[0]);
  87. free_irq(IRQ_EINT4,&pins_desc[1]);
  88. free_irq(IRQ_EINT2,&pins_desc[2]);
  89. free_irq(IRQ_EINT0,&pins_desc[3]);
  90. return 0;
  91. }
  92. static unsigned int fourth_drv_poll(struct file *file, poll_table *wait)
  93. {
  94. unsigned int mask = 0;
  95. /* 该函数,只是将进程挂在button_waitq队列上,而不是立即休眠 */
  96. poll_wait(file, &button_waitq, wait);
  97. /* 当没有按键按下时,即不会进入按键中断处理函数,此时ev_press = 0
  98. * 当按键按下时,就会进入按键中断处理函数,此时ev_press被设置为1
  99. */
  100. if(ev_press)
  101. {
  102. mask |= POLLIN | POLLRDNORM;  /* 表示有数据可读 */
  103. }
  104. /* 如果有按键按下时,mask |= POLLIN | POLLRDNORM,否则mask = 0 */
  105. return mask;
  106. }
  107. /* File operations struct for character device */
  108. static const struct file_operations fourth_drv_fops = {
  109. .owner      = THIS_MODULE,
  110. .open       = fourth_drv_open,
  111. .read       = fourth_drv_read,
  112. .release    = fourth_drv_close,
  113. .poll       = fourth_drv_poll,
  114. };
  115. /* 驱动入口函数 */
  116. static int fourth_drv_init(void)
  117. {
  118. /* 主设备号设置为0表示由系统自动分配主设备号 */
  119. major = register_chrdev(0, "fourth_drv", &fourth_drv_fops);
  120. /* 创建fourthdrv类 */
  121. fourthdrv_class = class_create(THIS_MODULE, "fourthdrv");
  122. /* 在fourthdrv类下创建buttons设备,供应用程序打开设备*/
  123. fourthdrv_device = device_create(fourthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons");
  124. return 0;
  125. }
  126. /* 驱动出口函数 */
  127. static void fourth_drv_exit(void)
  128. {
  129. unregister_chrdev(major, "fourth_drv");
  130. device_unregister(fourthdrv_device);  //卸载类下的设备
  131. class_destroy(fourthdrv_class);     //卸载类
  132. }
  133. module_init(fourth_drv_init);  //用于修饰入口函数
  134. module_exit(fourth_drv_exit);  //用于修饰出口函数
  135. MODULE_AUTHOR("LWJ");
  136. MODULE_DESCRIPTION("Just for Demon");
  137. MODULE_LICENSE("GPL");  //遵循GPL协议

应用测试源码:

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <fcntl.h>
  5. #include <unistd.h>
  6. #include <poll.h>
  7. /* fourth_test
  8. */
  9. int main(int argc ,char *argv[])
  10. {
  11. int fd;
  12. unsigned char key_val;
  13. struct pollfd fds;
  14. int ret;
  15. fd = open("/dev/buttons",O_RDWR);
  16. if (fd < 0)
  17. {
  18. printf("open error\n");
  19. }
  20. fds.fd = fd;
  21. fds.events = POLLIN;
  22. while(1)
  23. {
  24. /* A value of 0 indicates  that the call timed out and no file descriptors were ready
  25. * poll函数返回0时,表示5s时间到了,而这段时间里,没有事件发生"数据可读"
  26. */
  27. ret = poll(&fds,1,5000);
  28. if(ret == 0)
  29. {
  30. printf("time out\n");
  31. }
  32. else    /* 如果没有超时,则读出按键值 */
  33. {
  34. read(fd,&key_val,1);
  35. printf("key_val = 0x%x\n",key_val);
  36. }
  37. }
  38. return 0;
  39. }

测试步骤:

  1. [WJ2440]# ls
  2. Qt             etc            lib            sbin           third_test
  3. TQLedtest      first_drv.ko   linuxrc        sddisk         tmp
  4. app_test       first_test     mnt            second_drv.ko  udisk
  5. bin            fourth_drv.ko  opt            second_test    usr
  6. dev            fourth_test    proc           sys            var
  7. driver_test    home           root           third_drv.ko   web
  8. [WJ2440]# insmod fourth_drv.ko
  9. [WJ2440]# lsmod
  10. fourth_drv 3164 0 - Live 0xbf003000
  11. [WJ2440]# ls /dev/buttons -l
  12. crw-rw----    1 root     root      252,   0 Jan  2 03:00 /dev/buttons
  13. [WJ2440]# ./fourth_test
  14. time out
  15. time out
  16. key_val = 0x1
  17. key_val = 0x81
  18. key_val = 0x4
  19. key_val = 0x84
  20. key_val = 0x3
  21. key_val = 0x83
  22. key_val = 0x2
  23. key_val = 0x82
  24. ^C
  25. [WJ2440]# ./fourth_test &
  26. [WJ2440]# time out
  27. time out
  28. time out
  29. [WJ2440]#top
  30. Mem: 10076K used, 50088K free, 0K shrd, 0K buff, 7224K cached
  31. CPU:  0.1% usr  0.7% sys  0.0% nic 99.0% idle  0.0% io  0.0% irq  0.0% sirq
  32. Load average: 0.00 0.00 0.00 1/23 637
  33. PID  PPID USER     STAT   VSZ %MEM CPU %CPU COMMAND
  34. 637   589 root     R     2092  3.4   0  0.7 top
  35. 589     1 root     S     2092  3.4   0  0.0 -/bin/sh
  36. 1     0 root     S     2088  3.4   0  0.0 init
  37. 590     1 root     S     2088  3.4   0  0.0 /usr/sbin/telnetd -l /bin/login
  38. 587     1 root     S     1508  2.5   0  0.0 EmbedSky_wdg
  39. 636   589 root     S     1432  2.3   0  0.0 ./fourth_test
  40. 573     2 root     SW<      0  0.0   0  0.0 [rpciod/0]
  41. 5     2 root     SW<      0  0.0   0  0.0 [khelper]
  42. 329     2 root     SW<      0  0.0   0  0.0 [nfsiod]
  43. 2     0 root     SW<      0  0.0   0  0.0 [kthreadd]
  44. 3     2 root     SW<      0  0.0   0  0.0 [ksoftirqd/0]
  45. 4     2 root     SW<      0  0.0   0  0.0 [events/0]
  46. 11     2 root     SW<      0  0.0   0  0.0 [async/mgr]
  47. 237     2 root     SW<      0  0.0   0  0.0 [kblockd/0]
  48. 247     2 root     SW<      0  0.0   0  0.0 [khubd]
  49. 254     2 root     SW<      0  0.0   0  0.0 [kmmcd]
  50. 278     2 root     SW       0  0.0   0  0.0 [pdflush]
  51. 279     2 root     SW       0  0.0   0  0.0 [pdflush]
  52. 280     2 root     SW<      0  0.0   0  0.0 [kswapd0]
  53. 325     2 root     SW<      0  0.0   0  0.0 [aio/0]

由测试结果可以看出,当按键没有被按下时,5秒之后,会显示出time out,表示时间已到,在这5秒时间里,没有按键被按下,即没有数据可读,当按键按下时,立即打印出按下的按键;同时,fourth_test进程,也几乎不占用CPU的利用率。

linux字符驱动之poll机制按键驱动的更多相关文章

  1. Linux驱动之poll机制的理解与简单使用

    之前在Linux驱动之按键驱动编写(中断方式)中编写的驱动程序,如果没有按键按下.read函数是永远没有返回值的,现在想要做到即使没有按键按下,在一定时间之后也会有返回值.要做到这种功能,可以使用po ...

  2. 字符设备驱动笔记——poll机制分析(七)

    poll机制分析 所有的系统调用,基于都可以在它的名字前加上“sys_”前缀,这就是它在内核中对应的函数.比如系统调用open.read.write.poll,与之对应的内核函数为:sys_open. ...

  3. ARM Linux 驱动Input子系统之按键驱动测试

    上一篇已经谈过,在现内核的中引入设备树之后对于内核驱动的编写,主要集中在硬件接口的配置上了即xxxx.dts文件的编写. 在自己的开发板上移植按键驱动: 1.根据开发板的原理图 确定按键的硬件接口为: ...

  4. 字符驱动程序之——poll机制

    关于这个韦老师给了一个简单的参考文档: poll机制分析 韦东山 2009.12.10 所有的系统调用,基本都可以在它的名字前加上“sys_”前缀,这就是它在内核中对应的函数.比如系统调用open.r ...

  5. 字符设备之poll机制

    poll机制作用:相当于一个定时器.时间到了还没有资源就唤醒进程. 主要用途就是:进程设置一段时间用来等待资源,假设时间到了资源还没有到来,进程就立马从睡眠状态唤醒不再等待.当然这仅仅是使用于这段时间 ...

  6. 浅析Linux字符设备驱动程序内核机制

    前段时间在学习linux设备驱动的时候,看了陈学松著的<深入Linux设备驱动程序内核机制>一书. 说实话.这是一本非常好的书,作者不但给出了在设备驱动程序开发过程中的所须要的知识点(如对 ...

  7. 字符设备驱动笔记——中断方式按键驱动之linux中断处理结构(五)

    一.单片机下的中断处理 )分辨是哪一个中断 )调用处理函数 )清中断 二.linux下的中断处理 1)/arch/arm/kernel/irq.c asmlinkage void __exceptio ...

  8. 字符设备驱动笔记——中断方式按键驱动之linux异常处理结构(四)

    .中断方式获取按键值 单片机: )按键按下 )cup发生中断,跳转到异常向量入口执行 )b 函数 a.保存被中断的现场 b.执行中断处理函数 c.恢复 linux: )trap_init()函数构造异 ...

  9. 3.字符设备驱动------Poll机制

    1.poll情景描述 以之前的按键驱动为例进行说明,用阻塞的方式打开按键驱动文件/dev/buttons,应用程序使用read()函数来读取按键的键值. ) { read(fd, &key_v ...

随机推荐

  1. app自动化配置信息

    caps={    "platformName":"Android",#平台名称    "platformVersion":"6. ...

  2. IIR数字滤波器

    对于N阶IIR的计算方程式为: 一阶 Y(n)=a∗X(n)+(1−a)∗Y(n−1) 二阶 y[n]=b0⋅x[n]+b1⋅x[n−1]+b2⋅x[n−2]−a1⋅y[n−1]−a2⋅y[n−2]

  3. function语句注意事项

    function语句 在Javascript中定义一个函数,有两种写法: function foo() { } 和 var foo = function () { } 两种写法完全等价.但是在解析的时 ...

  4. web.config中配置数据库连接的两种方式

    在ASP.NET中有两种配置数据库连接代码的方式,它们分别是 appSettings 和 connectionStrings .在使用 appSettings 和 connectionStrings ...

  5. 从零实现一个http服务器

    我始终觉得,天生的出身很重要,但后天的努力更加重要,所以如今的很多“科班”往往不如后天努力的“非科班”.所以,我们需要重新给“专业”和“专家”下一个定义:所谓专业,就是别人搞你不搞,这就是你的“专业” ...

  6. nginx代理yum

    适用场景:有多台服务器,但是只有1台服务器可以出公网,此时即可使用如下方式,进行yum代理,解决内网服务器不能yum的尴尬. 一.首先需要把/etc/yum.repos.d下的文件备份到bak,然后留 ...

  7. 1. 调整InnoDB系统表空间的大小

    1. 调整InnoDB系统表空间的大小 介绍如何增大或减小InnoDB系统表空间的大小 . 1.1 增加InnoDB系统表空间大小 增加InnoDB系统空间最简单的方法就是,在配置文件中配置autoe ...

  8. cobbler 无人值守-介绍

    cobbler 介绍 快速网络安装linux操作系统的服务,支持众多的Linux版本,也支持网络安装windows系统 PXE的二次封装,将多种安装参数封装到一个菜单 它是由Python编写的 还可以 ...

  9. 查询SYS_ORG_TB树的层级

    WITH N(SYS_ORG_ID,SYS_ORG_NAME,LEVEL) AS( AS LEVEL FROM SYS_ORG_TB WHERE SYS_ORG_UPID IS NULL UNION ...

  10. Numpy的基本操作和相关概念(一)

    基础操作 np.random.randn() 符合正态分布的数据 np.vstack((a,b)) 矩阵水平拼接 np.hstack((a,b)) 矩阵垂直拼接 np.dot(a,c) 点阵积 A@B ...