1. 程式人生 > >Note07_Key按鍵驅動_共享中斷及中斷上下半部機制

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

        無;