一起分析Linux系统设计思想——05字符设备驱动之按键驱动
Posted 穿越临界点
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一起分析Linux系统设计思想——05字符设备驱动之按键驱动相关的知识,希望对你有一定的参考价值。
在学习资料满天飞的大环境下,知识变得非常零散,体系化的知识并不多,这就导致很多人每天都努力学习到感动自己,最终却收效甚微,甚至放弃学习。我的使命就是过滤掉大量的无效信息,将知识体系化,以短平快的方式直达问题本质,把大家从大海捞针的痛苦中解脱出来。
接前一篇,本篇在应用层使用异步机制来实现按键驱动程序编写。
3 应用层使用异步机制实现按键驱动
使用的异步机制是信号(signal)。
软中断信号(signal,又简称为信号)用来通知进程发生了异步事件。进程之间可以互相通过系统调用kill发送软中断信号。内核也可以因为内部事件而给进程发送信号,通知进程发生了某个事件。
内核处理一个进程收到的软中断信号是在该进程的上下文中。如果要捕捉的信号发生于进程正在一个系统调用中时,并且该进程睡眠在可中断的优先级上,这时该信号引起进程作一次longjmp,跳出睡眠状态,返回用户态并执行信号处理例程。
Tips:内核中信号的实现比较巧妙,后续单独介绍,这里先以用为主。
使用信号和使用硬件中断类似,包含几个重要的步骤:
- 中断注册和使能。
- 中断产生。
- 中断处理。
3.1 应用层代码
应用层代码完成上述步骤中的1和3。借助fcntl-F_SETOWN/F_GETFL/F_SETFL完成中断的注册和使能,主要是通过file结构告诉内核自己的pid,中断产生时需要使用。借助signal函数注册中断处理函数,真正的中断处理函数是运行在中断上下文中的,但是signal比较特殊,它运行在用户进程上下文中。
万事具备,只欠中断了。
1 #include <stdio.h>
2 #include <fcntl.h>
3 #include <signal.h>
4 typedef void (*sighandler_t)(int);
5
6
7 int fd = 0;
8
9 void key_handler(int signum)
10 {
11 int val = 0;
12
13 printf("signum = %d\\n", signum);
14
15 if (read(fd, &val, sizeof(val)) < 0) {
16 printf("read failed. \\n");
17 perror("read");
18 } else {
19 if (val)
20 printf("key pressed, val = %d \\n", val);
21
22 }
23 }
24
25 int main(void)
26 {
27 int flags = 0;
28
29 fd = open("/dev/key0", O_RDWR);
30 if (fd < 0) {
31 printf("open /dev/key_dev failed!\\n");
32 return -1;
33 }
34 signal(SIGIO, key_handler); //注册中断后处理函数
35 fcntl(fd, F_SETOWN, getpid()); //config 告诉内核中断的对象
36 flags = fcntl(fd, F_GETFL);
37 fcntl(fd, F_SETFL, flags | FASYNC); //会调用内核驱动中的fasync函数
38
39 for(;;) {
40 /*do something*/
41 usleep(20 * 1000);
42 }
43
44 (void)close(fd);
45
46 return 0;
47 }
3.2 内核驱动代码
为了代码完整性和方便拷贝运行,内核驱动的源码全部贴出来了。
其中singal的操作有两处。一处是key_fasync函数,是应用层代码的延续,延续中断的注册过程;另一处是在硬件中断服务例程中调用kill_fasync函数发送信号给应用层。其中key_fap全局变量是一个桥梁,将这两处连接起来,搞明白这个变量就搞懂了整个操作的逻辑。
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 #include <linux/fcntl.h>
11
12 #define GPFCON_ADD_BASE 0x56000050
13
14 volatile unsigned int *pgpfcon = NULL;
15 volatile unsigned int *pgpfdata = NULL;
16 DECLARE_WAIT_QUEUE_HEAD(key_wait_q);
17 volatile int event_press = 0;
18 struct fasync_struct *key_fap = NULL;
19
20 irqreturn_t keys_handler(int irq, void *dev_id)
21 {
22 printk("irq = %d is triggered. \\n", irq);
23 /* Wake up the blocked thread */
24 event_press = 1;
25 //wake_up_interruptible(&key_wait_q);
26 kill_fasync(&key_fap, SIGIO, POLL_IN); /*发送信号给应用层*/
27
28 return IRQ_HANDLED;
29 }
30
31 int cdriver_open(struct inode *inode, struct file *file)
32 {
33 int minor = MINOR(inode->i_rdev);
34
35 printk("cdriver open success!\\n");
36
37 request_irq(IRQ_EINT0, keys_handler, SA_TRIGGER_RISING, "key0", 1);
38
39 return 0;
40 }
41
42 int cdriver_release(struct inode *inode, struct file *file)
43 {
44 printk("cdriver released. \\n");
45
46 free_irq(IRQ_EINT0, 1);
47
48 return 0;
49 }
50
51 int key_fasync(int fd, struct file *filp, int on)
52 {
53 return fasync_helper(fd, filp, on, &key_fap);
54
55 }
56 ssize_t key_read(struct file *file, char __user *user_buff, size_t len, loff_t *offset)
57 {
58 /* Block current thread. */
59 if (copy_to_user(user_buff, &event_press, sizeof(event_press)) < 0)
60 return -EFAULT;
61 event_press = 0;
62 //wait_event_interruptible(key_wait_q, event_press);
63
64 return 0;
65 }
66
67 struct file_operations cdriver_fops = {
68 .owner = THIS_MODULE,
69 .open = cdriver_open,
70 .read = key_read,
71 .release = cdriver_release,
72 .fasync = key_fasync,
73 };
74
75 int major = 0;
76 struct class *led_class = NULL;
77 struct class_device *led_class_dev[3] = {NULL};
78
79 int __init cdriver_init(void)
80 {
81 int minor = 0;
82
83 major = register_chrdev(0, "key_driver", &cdriver_fops);
84
85 led_class = class_create(THIS_MODULE, "keys");
86 if (NULL == led_class)
87 return -EINVAL;
88 for (minor = 0; minor < 3; minor++) {
89 led_class_dev[minor] = class_device_create(led_class, NULL, MKDEV(major, minor), NULL, "key%d", minor);
90 if (NULL == led_class_dev[minor])
91 return -EINVAL;
92 }
93
94 return 0;
95 }
96
97 void __exit cdriver_exit(void)
98 {
99 int minor = 0;
100
102 unregister_chrdev(major, "key_driver");
103 for (minor = 0; minor < 3; minor++)
104 class_device_unregister(led_class_dev[minor]);
105 class_destroy(led_class);
107 }
108
109 module_init(cdriver_init);
110 module_exit(cdriver_exit);
111 MODULE_LICENSE("GPL");
按键驱动就暂时告一段落了。下一篇剖析中断框架,对按键驱动做一个升华。
写博客写的越多越深深地体会到,不可能事无巨细地把一个内容完全在一篇博客甚至是一个系列中说的很明白,即使是说明白了,篇幅也会极具膨胀,读者的感受也不好,一般读到一半就撤了,这种长篇巨著价值感并不是很强。因为,嵌入式、操作系统、架构设计、编程语言等等是一个体系,需要点滴积累,反复迭代。
曲径通幽,与君共勉!!!
恭喜你又坚持看完了一篇博客,又进步了一点点!如果感觉还不错就点个赞再走吧,你的点赞和关注将是我持续输出的哒哒哒动力~~
以上是关于一起分析Linux系统设计思想——05字符设备驱动之按键驱动的主要内容,如果未能解决你的问题,请参考以下文章
一起分析Linux系统设计思想——05字符设备驱动之按键驱动