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 }
-
遺留問題
無;