一起分析Linux系统设计思想——05字符设备驱动之按键驱动

Posted 穿越临界点

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一起分析Linux系统设计思想——05字符设备驱动之按键驱动相关的知识,希望对你有一定的参考价值。

在学习资料满天飞的大环境下,知识变得非常零散,体系化的知识并不多,这就导致很多人每天都努力学习到感动自己,最终却收效甚微,甚至放弃学习。我的使命就是过滤掉大量的无效信息,将知识体系化,以短平快的方式直达问题本质,把大家从大海捞针的痛苦中解脱出来。

1 实现按键驱动的两种方式

按键驱动程序包括两次信息交互过程。一次是外围硬件(GPIO引脚)与内核代码的交互;一次是内核代码与应用层代码的交互。

外围硬件与内核代码的交互直接使用异步机制——硬件中断。

内核代码与应用层代码的交互可以使用同步机制——睡眠与唤醒(不使用轮循与退出);也可以使用异步机制——软件中断。据此,实现按键驱动可以有两种方式。

今天先介绍第一种方式。

2 使用同步机制实现按键驱动

下面我们就使用 睡眠与唤醒 同步机制实现按键驱动功能。

2.1 内核驱动程序

如下为按键内核驱动程序。程序在LED设备驱动的基础上进行改进而来。

最重要的改动就是在open函数中调用了 request_irq() 函数,来进行中断的申请和中断处理函数的注册。

另外一个改动就是在read函数中调用 wait_event_interruptible() 函数,在条件未满足时将调用read函数的线程 阻塞 ,并放入阻塞队列中等待条件满足时被唤醒。

中断处理函数中做的最主要的事情就是 唤醒 在阻塞队列中等待的线程。

Tips:这里留下了一个非常关键的问题——申请中断时中断号是如何确定的。这个问题涉及到了Linux系统的中断架构的内容,等到按键驱动例程完成之后专门进行介绍。

  1 #include <linux/module.h>
  2 #include <linux/kernel.h>
  3 #include <linux/fs.h>
  4 #include <linux/init.h>
  5 #include <asm/io.h>         /* ioremap()  */
  6 #include <linux/uaccess.h>
  7 #include <linux/device.h>
  8 #include <linux/interrupt.h>
  9 #include <linux/irqreturn.h>
 10
 11 #define GPFCON_ADD_BASE 0x56000050
 12
 13 volatile unsigned int *pgpfcon = NULL;
 14 volatile unsigned int *pgpfdata = NULL;
 15 DECLARE_WAIT_QUEUE_HEAD(key_wait_q); /*定义等待队列*/
 16 volatile int event_press = 0;
 17
 18 irqreturn_t keys_handler(int irq, void *dev_id) /*中断处理函数*/
 19 {
 20     printk("irq = %d is triggered. \\n", irq);
 21     /* Wake up the blocked thread */
 22     event_press = 1; /*修改事件标记*/
 23     wake_up_interruptible(&key_wait_q); /*唤醒等待队列中的线程*/
 24
 25     return IRQ_HANDLED;
 26 }
 27 int cdriver_open(struct inode *inode, struct file *file)
 28 {
 29     int minor = MINOR(inode->i_rdev);
 30
 31     printk("cdriver open success!\\n");
 38     /* 申请中断 */
 39     request_irq(IRQ_EINT0, keys_handler, SA_TRIGGER_RISING, "key0", 1);
 40
 41     return 0;
 42 }
 43
 44 int cdriver_release(struct inode *inode, struct file *file)
 45 {
 46     printk("cdriver released. \\n");
 47
 48     free_irq(IRQ_EINT0, 1); /*释放中断号*/
 49
 50     return 0;
 51 }
 52 ssize_t key_read(struct file *file, char __user *user_buff, size_t len, loff_t *offset)
 53 {
 54     /* Block current thread. */
 55     event_press = 0;
 56     wait_event_interruptible(key_wait_q, event_press); /*将当前线程阻塞并放入等待队列*/
 57
 58     return 0;
 59 }
 60
 
 84 struct file_operations cdriver_fops = {
 85     .owner = THIS_MODULE,
 86     .open = cdriver_open,
 88     .read = key_read,
 89     .release = cdriver_release,
 90 };
 91
 92 int major = 0;
 93 struct class *led_class = NULL;
 94 struct class_device *led_class_dev[3] = {NULL};
 95
 96 int __init cdriver_init(void)
 97 {
 98     int minor = 0;
 99
100     major = register_chrdev(0, "key_driver", &cdriver_fops);
101
102     led_class = class_create(THIS_MODULE, "keys");
103     if (NULL == led_class)
104         return -EINVAL;
105     for (minor = 0; minor < 3; minor++) {
106         led_class_dev[minor] = class_device_create(led_class, NULL, MKDEV(major, minor), NULL, "key%d", minor);
107         if (NULL == led_class_dev[minor])
108             return -EINVAL;
109     }
116     return 0;
117 }
118
119 void __exit cdriver_exit(void)
120 {
121     int minor = 0;
122
124
125     unregister_chrdev(major, "key_driver");
126     for (minor = 0; minor < 3; minor++)
127         class_device_unregister(led_class_dev[minor]);
128     class_destroy(led_class);
129
130 }
131
132 module_init(cdriver_init);
133 module_exit(cdriver_exit);
134 MODULE_LICENSE("GPL");

2.2 应用程序实现

应用主要用来验证内核驱动程序的功能,所以比较简单。主要就是阻塞等待按键的到来。

  1 #include <stdio.h>
  2 #include <fcntl.h>
  3
  4 int main(void)
  5 {
  6     int fd = 0;
  7     int val = 0xff;
  8
 13
 14     fd = open("/dev/key0", O_RDWR);
 15     if (fd < 0) {
 16         printf("open /dev/key_dev failed!\\n");
 17         return -1;
 18     }
 19
 20     for(;;) {/*调用read时会阻塞等待,直到有按键中断到来*/
 21             if (read(fd, &val, sizeof(val)) < 0) { 
 22                 printf("read failed. \\n");
 23                 perror("read");
 24             } else {
 25                 printf("key pressed \\n");
 26             }
 27     }
 28
 29     (void)close(fd);
 30
 31     return 0;
 32 }

一个最简单的基于硬件中断的按键中断程序其实已经完成了。

但是到现在其实还有几个问题没有解决。按键消抖怎么做,在哪里做?内核驱动程序如何支持多个物理按键?如何选取申请的中断号?

为减小大家的阅读压力,上面的问题我们就且听下回分解啦~


恭喜你又坚持看完了一篇博客,又进步了一点点!如果感觉还不错就点个赞再走吧,你的点赞和关注将是我持续输出的哒哒哒动力~~

以上是关于一起分析Linux系统设计思想——05字符设备驱动之按键驱动的主要内容,如果未能解决你的问题,请参考以下文章

一起分析Linux系统设计思想——05字符设备驱动之按键驱动

一起分析Linux系统设计思想——05字符设备驱动之按键驱动

一起分析Linux系统设计思想——05中断框架剖析

一起分析Linux系统设计思想——05中断框架剖析

一起分析Linux系统设计思想——05中断框架剖析

一起分析Linux系统设计思想——05中断框架剖析