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. 需要有几个共享中断就注册几次中断号,但必须确保中断服务函数不同、注册的中断名与中断号对应、且传入中断服务函数的参数不能为空;那么中断发生时,会按照中断注册的顺序执行中断服务函数;

扫描二维码关注公众号,回复: 4028714 查看本文章

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
[root@milletapp_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 }
  • 遗留问题

        无;

 

 

 

 

 

 

 

猜你喜欢

转载自blog.csdn.net/llzhang_fly/article/details/83958111