Note07_Key按键驱动_共享中断及中断上下半部机制
-
共享中断机制:
1)共享中断
即对于同一个中断源的1次触发,会同时按某个顺序有两个或两个以上的中断处理响应,也就是多个处理函数共享同一个中断号。
2)若需设置共享中断,则:
中断申请函数:
ret = request_irq( irqnum, do_key_handler,
IRQF_SHARED | IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING,
Irq_name, &irq_data );
A. 在注册中断时,中断标记flags要包含TRQF_SHARED共享方式,同时注册中断的第五个参数irq_data(即传入中断处理函数的参数)不能为NULL,否则插入驱动程序时,会报错;
insmod: can't insert 'demo.ko': invalid parameter
B. 需要有几个共享中断就注册几次中断号,但必须确保中断服务函数不同、注册的中断名与中断号对应、且传入中断服务函数的参数不能为空;那么中断发生时,会按照中断注册的顺序执行中断服务函数;
3)若中断注册失败时,使用free()函数释放中断资源;
释放中断函数:
Void free_irq( unsigned int irq, void *dev_id );
4)举例说明:
比如注册一个中断处理函数
struct millkey{
int irqnum;
char *name;
int keycnt;
}keys[] = {
{ IRQ_EINT(28), "KEY1", 0 },
{ IRQ_EINT(29), "KEY2", 0 },
};
/*
** shared irq register, you will register one irqnum to two different
** irq handlers, eg: do_key_handle1 and do_key_handler2 func;
*/
static int register_keys(void)
{
int i;
int ret;
for (i = 0; i < ARRAY_SIZE(keys); ++i) {
ret = request_irq(
keys[i].irqnum,
do_key_handler1,
IRQF_SHARED | IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING,
keys[i].name,
&keys[i] // irq handler1's agr is a address;
);
if (ret < 0) {
goto error0;
}
ret = request_irq(
keys[i].irqnum,
do_key_handler2,
IRQF_SHARED | IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING,
keys[i].name,
(void *)(i*2) // irq handler2's arg is a data;
);
if (ret < 0) {
free_irq(keys[i].irqnum, &keys[i]);
goto error0;
}
}
return 0;
error0:
while (i--) {
free_irq(keys[i].irqnum, &keys[i]);
free_irq(keys[i].irqnum, (void *)(i*2));
}
return ret;
}
-
中断上下半部实现机制
中断上半部——响应中断信号;
中断下半部——处理相应的中断服务;
1)中断的tasklet机制
a. 定义Tasklet结构对象,定义中断下半部处理函数:
Struct tasklet_struct task;
Void my_tasklet_func(unsigned long);
tasklet_struct结构体说明:
Struct tasklet_struct{
Struct tasklet_struct *next;
Unsigned long state;
Aromic_t count;
Void (* func) (unsigned long); //下半部处理函数
Unsigned long data; //下半部处理函数的参数
}
b. 初始化:将定义tasklet 结构对象及其处理函数关联起来
Tasklet_init( &task, my_tasklet_func, data);
// 实现将定义的名称为task的tasklet结构与my_tasklet_func()函数绑定,并将data数据传入这个函数;
注:以上a 和 b 两个步,可使用如下函数来实现:
Void my_tasklet_func(unsigned long);
DECLARE_TASKLET( task, my_tasklet_func(, data );
c. 使用如下函数,实现中断下半部任务调度的设置:
Tasklet_shedule(&task);
// 在需要调度tasklet的时候,引用Tasklet_shedule()函数,即可实现使系统在适当的时候进行调度中断下半部。
一般在中断的上半部函数中引用设置;
2)中断workqueue机制
工作队列使用方法和tasklet 类似:
a. 定义工作队列,定义中断下半部执行函数:
Struct work_struct my_wq;
Void my_wq_func( unsigned long );
b. 初始化工作队列,并将工作队列和处理函数绑定:
INIT_WORK( &my_wq, ( void (*) (void *) ) my_wq_func, NULL );
c. 中断上半部中,设置调度工作队列函数:
Schedule_work( &my_wq );
3)软中断(irq)
是一种传统的底半部处理机制,其执行时机为上半部函数返回的时候;(tasklet 是一种基于软中断实现的中断下半部机制,同时也运行于软中断上下文)
a. Softirq_action结构体表示一个软中断:包含软中断处理函数指针和传递给该函数的参数;
b. 使用open_softirq() 函数可以注册软中断对应的处理函数;
c. Raise_softirq() 函数可以触发一个软中断;
|
软中断 |
Tasklet |
Workqueue |
概念 |
中断下半部(底半部)的一种处理机制; |
|
|
运行速度 |
快 |
快 |
慢 |
上下文 |
运行在中断上下文 |
运行在中断上下文 |
运行在进程上下文 |
是否可睡眠 |
绝不允许 |
绝不允许 |
可睡眠 |
|
软中断和tasklet 运行与软中断上下文,属于原子上下文的一种;所以不允许休眠 |
工作队列运行于进程上下文,则允许休眠 |
|
信号:类似于中断,于中断的区别 |
信号,是异步通知; 1)硬中断:外部设备对CPU 的中断; 2)软中断:中断下半部(底半部)的一种处理机制; 3)信号:由内核或者其他进程对某个进程的中断; 4)系统调用场合,说的通过软中断(arm 是swi)陷入内核,这里的软中断指的是由软件指令引发的中断。 |
如何测试程序运行在进程上下文中还是中断上下文?
#define hardirq_count() (preempt_count() & HARDIRQ_MASK)
#define softirq_count() (preempt_count() & SOFTIRQ_MASK)
#define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK | NMI_MASK))
#define in_irq() (hardirq_count()) // 判断当前是否在硬件中断上下文
#define in_softirq() (softirq_count()) // 判断当前是否在软件中断上下文
#define in_interrupt() (irq_count()) // 判断当前是否在中断状态(硬中断或软中断、上下半部)
-
标准按键设备驱动实现
1)驱动入口:
a. 注册杂项设备驱动
b. 按键中断申请资源
c. 初始化等待队列; // 应用层读函数,在内核中阻塞,当有数据可读时,才被唤醒读取
d. 注册中断下半部处理函数机制:tasklet
2)驱动出口:
a. 移除按键注册的资源
b. 释放注册的misc 杂项设备驱动
c. 移除注册的中断下半部处理机制;
3)驱动fops 实现函数集合
a. 中断上半部响应中断信号; 并使用tasklet_schedule() 函数设置系统调用中断下半部
b. 中断下半部处理中断信号:识别时那个按键按下、松开;
并设置按键的状态、唤醒系统read 函数
c. 内核read 函数实现,等待有中断且条件满足时,则唤醒,拷贝数据到应用层;
总结
1)软中断和tasklet机制,绝不运行休眠;
2)按键驱动中,获取按键状态时,若当前的状态没有变化,则不进行处理,这样就只有1次按键触发,不会产生因按键抖动而产生多次中断的现象;
3)应用层采用read() 函数获取按键的状态,但在内核中,若当前按键未触发,则read() 函数应该处于等待状态,直到按键状态可读时,再唤醒,将当前的按键状态传递给应用层的read函数;
4)内核中断参数传递:注册中断时,传递某按键中断资源首地址给中断上半部函数;中断上半部中,获取参数,传递给task.data 变量,该变量是 tasklet机制中,将该参数传递给中断下半部函数; 所以也就间接的将中断发生时的参数传递给中断下半部处理函数了;
-
测试结果
‵‵‵c
[[email protected]_0926]# ./test_app /dev/millkey
[ 4264.390000] KER-[do_th_handler] key trigged is 3206972620 !
[ 4264.390000] KER-[do_th_handler] key trigged key num is 0 !
[ 4264.390000] KER-[do_bh_handler] key trigged is 3206972620 !
[ 4264.390000] KER-[do_bh_handler] key trigged key num is 0 !
[ 4264.390000] KER-[do_bh_handler] key keybuf[pdev-num] = 5 !
[ 4264.400000] KER-[mill_read], send keybuf[i] = 5
[ 4264.400000] KER-[mill_read], send keybuf[i] = 0
[ 4264.405000] KER-[mill_read], send keybuf[i] = 0
[ 4264.410000] KER-[mill_read], send keybuf[i] = 0
key 0 is down[5]!
[ 4264.505000] KER-[do_th_handler] key trigged is 3206972620 !
[ 4264.505000] KER-[do_th_handler] key trigged key num is 0 !
[ 4264.505000] KER-[do_bh_handler] key trigged is 3206972620 !
[ 4264.505000] KER-[do_bh_handler] key trigged key num is 0 !
[ 4264.505000] KER-[do_bh_handler] key keybuf[pdev-num] = 15 !
[ 4264.510000] KER-[mill_read], send keybuf[i] = 15
[ 4264.515000] KER-[mill_read], send keybuf[i] = 0
[ 4264.520000] KER-[mill_read], send keybuf[i] = 0
[ 4264.525000] KER-[mill_read], send keybuf[i] = 0
key 0 is up[15]!
[ 4270.630000] KER-[do_th_handler] key trigged is 3206972636 !
[ 4270.630000] KER-[do_th_handler] key trigged key num is 1 !
[ 4270.630000] KER-[do_bh_handler] key trigged is 3206972636 !
[ 4270.630000] KER-[do_bh_handler] key trigged key num is 1 !
[ 4270.630000] KER-[do_bh_handler] key keybuf[pdev-num] = 5 !
[ 4270.635000] KER-[mill_read], send keybuf[i] = 0
[ 4270.640000] KER-[mill_read], send keybuf[i] = 5
[ 4270.645000] KER-[mill_read], send keybuf[i] = 0
[ 4270.650000] KER-[mill_read], send keybuf[i] = 0
key 1 is down[5]!
[ 4270.775000] KER-[do_th_handler] key trigged is 3206972636 !
[ 4270.775000] KER-[do_th_handler] key trigged key num is 1 !
[ 4270.775000] KER-[do_bh_handler] key trigged is 3206972636 !
[ 4270.775000] KER-[do_bh_handler] key trigged key num is 1 !
[ 4270.775000] KER-[do_bh_handler] key keybuf[pdev-num] = 15 !
[ 4270.785000] KER-[mill_read], send keybuf[i] = 0
[ 4270.785000] KER-[mill_read], send keybuf[i] = 15
[ 4270.790000] KER-[mill_read], send keybuf[i] = 0
[ 4270.795000] KER-[mill_read], send keybuf[i] = 0
key 1 is up[15]!
-
内核驱动函数
‵‵‵java
1 #include <linux/init.h>
2 #include <linux/uaccess.h>
3 #include <linux/module.h>
4 #include <linux/interrupt.h>
5 #include <linux/fs.h>
6 #include <linux/sched.h>
7 #include <linux/miscdevice.h>
8
9 #define DEVNAME "millkey"
10 /*
11 tasklet_struct结构体说明:
12
13 Struct tasklet_struct{
14 Struct tasklet_struct *next;
15 Unsigned long state;
16 Aromic_t count;
17 Void (* func) (unsigned long); //下半部处理函数
18 Unsigned long data; //下半部处理函数的参数
19 }
20 */
21
22 struct millkey{
23 int num;
24 int irqnum;
25 char *name;
26 int keycnt;
27 }keys[] = {
28 { 0, IRQ_EINT(26), "KEY1", 0 },
29 { 1, IRQ_EINT(27), "KEY2", 0 },
30 { 2, IRQ_EINT(28), "KEY3", 0 },
31 { 3, IRQ_EINT(29), "KEY4", 0 },
32 };
33
34 static char keybuf[4] = {0};
35
36 static struct tasklet_struct task;
37 static int dnup_flag = 0;
38 static wait_queue_head_t wait;
39
40 //ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
41 static ssize_t
42 mill_read (struct file *filp, char __user *buf, size_t cnt, loff_t *fpos)
43 {
44 int i = 0;
45
46 if (cnt != 4) {
47 return -EINVAL;
48 }
49 /*等待直到条件满足则唤醒*/
50 wait_event_interruptible(wait, dnup_flag != 0);
51
52 if (copy_to_user(buf, keybuf, cnt)) {
53 return -EINVAL;
54 }
55
56 for(i=0; i<4; i++)
57 {
58 printk("KER-[%s], send keybuf[i] = %d\n", __func__, keybuf[i]);
59 }
60
61 for(i=0; i<4; i++) {
62 if(keybuf[i] == 15) {
63 for(i=0; i<4; i++) {
64 keybuf[i] = 0;
65 }
66 break;
67 }
68 }
69
70 dnup_flag = 0;
71
72 return cnt;
73 }
74
75 static struct file_operations fops = {
76 .owner = THIS_MODULE,
77 .read = mill_read,
78 };
79
80 static struct miscdevice misc = {
81 .minor = MISC_DYNAMIC_MINOR,
82 .name = DEVNAME,
83 .fops = &fops,
84 };
85
86 /*中断下半部准备对应按键的状态*/
87 static void do_bh_handler(unsigned long data)
88 {
89 struct millkey *pdev = (void *)data;
90 printk("KER-[%s] key trigged is %lu !\n", __func__, data);
91 printk("KER-[%s] key trigged key num is %d !\n", __func__, (pdev->num));
92
93 // 偶数按下,奇数松开: key_buf[num]的值 作为按键按下0,按键松开1的标志,不再在表> 其他;
94 pdev->keycnt++;
95
96 if ((pdev->keycnt%2) && keybuf[pdev->num] != 0x5) {
97 keybuf[pdev->num] = 0x5;
98 dnup_flag = 1;
99 printk("KER-[%s] key keybuf[pdev-num] = %d !\n", __func__, keybuf[pdev->num]) ;
100 wake_up(&wait);
101 }
102 else if (!(pdev->keycnt%2) && keybuf[pdev->num] != 0xf){
103 keybuf[pdev->num] = 0xf;
104 dnup_flag = 1;
105 printk("KER-[%s] key keybuf[pdev-num] = %d !\n", __func__, keybuf[pdev->num]) ;
106 wake_up(&wait);
107 }
108 }
109
110 static irqreturn_t do_th_handler(int irqnum, void *data)
111 {
112 // task.data, 下半部处理函数的参数: 按键元素行首地址
113 task.data = (unsigned long)data;
114 //struct millkey *tmp_key = (struct millkey *)data;
115 struct millkey *tmp_key = data;
116
117 printk("KER-[%s] key trigged is %lu !\n", __func__, task.data);
118 printk("KER-[%s] key trigged key num is %d !\n", __func__, (tmp_key->num));
119 tasklet_schedule(&task);
120
121 return IRQ_HANDLED;
122 }
123
124 static int register_keys(void)
125 {
126 int i;
127 int ret;
128
129 for (i = 0; i < ARRAY_SIZE(keys); ++i) {
130 ret = request_irq(
131 keys[i].irqnum,
132 do_th_handler, // 上半部处理函数
133 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
134 keys[i].name,
135 &keys[i] // 传入中断上半部函数的入参:某个按键的首地址
136 );
137
138 if (ret < 0) {
139 goto error0;
140 }
141 }
142
143 //tasklet_init(&task, do_bh_handler, 0);
144
145 return 0;
146
147 error0:
148 while (i--) {
149 free_irq(keys[i].irqnum, &keys[i]);
150 }
151
152 return ret;
153 }
154
155 static void unregister_keys(void)
156 {
157 int i;
158
159 for (i = 0; i < ARRAY_SIZE(keys); ++i) {
160 free_irq(keys[i].irqnum, &keys[i]);
161 }
162
163 tasklet_kill(&task);
164 }
165
166 static int __init demo_init(void)
167 {
168 int ret;
169
170 ret = misc_register(&misc);
171 if (ret < 0) {
172 return ret;
173 }
174
175 ret = register_keys();
176
177 if (ret < 0) {
178 misc_deregister(&misc);
179 return ret;
180 }
181
182 init_waitqueue_head(&wait);
183 tasklet_init(&task, do_bh_handler, 0);
184
185 printk("KER-register [%s] device ok!\n", DEVNAME);
186
187 return 0;
188 }
189
190 module_init(demo_init);
191
192 static void __exit demo_exit(void)
193 {
194 unregister_keys();
195 misc_deregister(&misc);
196 tasklet_kill(&task);
197 }
198
199 module_exit(demo_exit);
200
201 MODULE_LICENSE("GPL");
202
203 MODULE_AUTHOR("zhang li lin");
204 MODULE_VERSION("zhang li lin 2018 11 11");
205 MODULE_DESCRIPTION("It is a example for get keys state module.");
-
应用层读取函数
```c
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include <unistd.h>
6 #include <assert.h>
7 #include <stdlib.h>
8 #include <string.h>
9
10 #include "ioctl.h"
11
12 void usage(const char *str)
13 {
14 fprintf(stderr, "Usage:\n");
15 fprintf(stderr, " %s device\n", str);
16 exit(1);
17 }
18
19 int main(int argc, char **argv)
20 {
21 int i;
22 int fd;
23 int ret;
24 char buf[4] = {10};
25 char oldbuf[4] = {0};
26
27 if (argc != 2) {
28 usage(argv[0]);
29 }
30
31 fd = open(argv[1], O_RDONLY);
32 assert(fd > 0);
33
34 for (;;) {
35 ret = read(fd, buf, 4);
36 for (i = 0; i < 4; i++) {
37
38
39 if (buf[i] == 0x5)
40 {
41 printf("key %d is %s[%d]!\n", i, "down", buf[i]);
42 buf[i] = 10;
43 }
44 else if (buf[i] == 0xf)
45 {
46 printf("key %d is %s[%d]!\n", i, "up", buf[i]);
47 buf[i] = 10;
48 }
49 }
50 }
51
52 return 0;
53 }
-
遗留问题
无;