驱动开发之select与中断下半部分

驱动开发之select与中断下半部分:

中断上半部、下半部:

1、已知中断应该尽快完成,但是很多时候不能保证中断代码一定会及时完成。
2、当不能保证中断尽快完成时,内核将曾经的一个中断处理函数分成了两个中断处理函数
  其中中断上半部用来处理紧急事件
  中断下半部用来处理非紧急事件
3、什么是紧急事件:
      直接操作硬件时
      对时间要求非常敏感
      不能被其他中断打断
4,中断下半部分类:
        软中断(需要使用软中断号,但是所有的软中断号都被占用了)

        小任务

        工作队列

小任务:对软中断的二次封装:

初始化小任务:

1 void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long), unsigned long data)
3 参数1:
4 参数2:中断下半部处理函数
5 参数3:给中断下半部处理函数传参的

调度小任务:

1 tasklet_schedule(struct tasklet_struct *t);

什么时候调用下半部?
        在中断上半部处理函数返回前,或者返回后调用。

  1 #include <linux/module.h>
  2 #include <linux/fs.h>
  3 #include <linux/platform_device.h>
  4 #include <linux/device.h>
  5 #include <asm/uaccess.h>
  6 #include <linux/irqreturn.h>
  7 #include <linux/interrupt.h>
  8 #include <linux/sched.h>
  9 
 10 int major;
 11 
 12 struct class *cls;
 13 struct device *devs;
 14 struct resource *res_key2;
 15 struct resource *res_key3;
 16 
 17 int key;
 18 wait_queue_head_t keyq;
 19 int flag = 0;
 20 struct timer_list t;
 21 int glo_irqno;
 22 
 23 struct tasklet_struct ts;
 24 
 25 irqreturn_t fs4412_key_handler(int irqno,void *id)
 26 {
 27     glo_irqno = irqno;
 28     mod_timer(&t,jiffies + 30);
 29     return IRQ_HANDLED;
 30 }
 31 
 32 void fs4412_key_timer_handler(unsigned long data)
 33 {
 34     if(glo_irqno == res_key2->start)
 35         key = 2;
 36     if(glo_irqno == res_key3->start)
 37         key = 3;
 38 //    wake_up(&keyq);
 39     wake_up_interruptible(&keyq);
 40     flag = 1;
 41     tasklet_schedule(&ts);
 42 }
 43 
 44 void fs4412_key_task(unsigned long data)
 45 {
 46     printk("task schedule
");
 47 }
 48 
 49 int fs4412_key_open(struct inode *inode ,struct file *filp)
 50 {
 51     return 0;
 52 }
 53 
 54 ssize_t fs4412_key_read(struct file *filp,char __user *ubuf,size_t size,loff_t *off)
 55 {
 56     int ret;
 57     
 58 //    wait_event(keyq,flag != 0);
 59     wait_event_interruptible(keyq,flag != 0);
 60     ret = copy_to_user(ubuf,&key,sizeof(key));
 61 
 62     flag = 0;
 63     return sizeof(key);
 64 }
 65 
 66 struct file_operations fops = {
 67     .owner = THIS_MODULE,
 68     .open = fs4412_key_open,
 69     .read = fs4412_key_read,
 70 };
 71 
 72 int fs4412_key_probe(struct platform_device *pdev)
 73 {
 74     int ret;
 75     printk("match ok
");
 76 
 77     major = register_chrdev(0,"key",&fops);
 78     cls = class_create(THIS_MODULE,"key");
 79     devs = device_create(cls,NULL,MKDEV(major,0),NULL,"key");
 80     
 81     res_key2 = platform_get_resource(pdev,IORESOURCE_IRQ,0);//索引interrupts = <>,<>第一个<>
 82     res_key3 = platform_get_resource(pdev,IORESOURCE_IRQ,1);
 83     ret = request_irq(res_key2->start,fs4412_key_handler,IRQF_TRIGGER_FALLING,"key2",NULL);
 84     ret = request_irq(res_key3->start,fs4412_key_handler,IRQF_TRIGGER_FALLING,"key3",NULL);
 85 
 86     init_waitqueue_head(&keyq);
 87 
 88     init_timer(&t);
 89     t.function = fs4412_key_timer_handler;//定时器中断处理函数
 90     add_timer(&t);//让内核认识定时器中断处理函数
 91 
 92     tasklet_init(&ts,fs4412_key_task,0);
 93     return 0;
 94 }
 95 
 96 int fs4412_key_remove(struct platform_device *pdev)
 97 {
 98     del_timer(&t);
 99     free_irq(res_key3->start,NULL);
100     free_irq(res_key2->start,NULL);
101     device_destroy(cls,MKDEV(major,0));
102     class_destroy(cls);
103     unregister_chrdev(major,"key");
104     return 0;
105 }
106 
107 struct of_device_id fs4412_dt_tbl[] = {
108     {
109         .compatible = "fs4412,key",
110     },
111     {},
112 };
113 
114 
115 struct platform_driver pdrv = {
116     .driver = {
117         .name = "fs4412-key",
118         .of_match_table = fs4412_dt_tbl,
119     },
120     .probe = fs4412_key_probe,
121     .remove = fs4412_key_remove,
122 };
123 
124 module_platform_driver(pdrv);
125 MODULE_LICENSE("GPL");
小任务

工作队列:

1 INIT_WORK(struct work_struct *,void (*)(struct work_struct *work))
2 
3 static inline bool schedule_work(struct work_struct *work) 

如何选择下半部的使用方法?
    小任务运行在中断上下文中(内核空间),对时间要求敏感
    工作队列运行在进程上下文中(用户空间和内核空间),对时间不敏感

    在工作队列中可以使用等待队列,互斥锁,信号量。小任务不可以。

1 应用层:
2 int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set*exceptfds, struct timeval *timeout);
3 参数1:文件描述符最大值+1
4 参数2:读表
5 参数3:写表
6 参数4:异常表
7 参数5:超时检测
8 返回值:出错返回-1,超时返回0,成功返回>0
9 功能:如果没有就绪的文件描述符则阻塞,如果有就绪的文件描述符则唤醒,唤醒的同时会将未就绪的文件描述符清空。
1 FD_SET();将指定文件描述符加入表中
2 FD_ZERO();清空文件描述符表
3 FD_CLR();删除指定的文件描述符
4 FD_ISSET();判断fd是否在表中
 1 内核源码分析:
 2 struct file_operations 
 3 {
 4 unsigned int (*poll) (struct file *, struct poll_table_struct *);//被select调用
 5 }
 6 
 7 vi fs/select.c 
 8 找到do_select函数
 9 mask = (*f_op->poll)(f.file, wait);
10 
11 poll_initwait(&table); 
12 
13 --->void poll_initwait(struct poll_wqueues *pwq) 
14 { 
15     init_poll_funcptr(&pwq->pt, __pollwait);
16     pwq->polling_task = current;//当前进程
17 } 
18 
19 --->static inline void init_poll_funcptr(poll_table *pt, poll_queue_proc qproc) 
20 { 
21      pt->_qproc = qproc;//将__pollwait赋值给了_qproc
22  } 
23 
24 如果想要调用__pollwait,需要使用
25 static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
26 {
27      if (p && p->_qproc && wait_address)
28     p->_qproc(filp, wait_address, p);//调用__pollwait
29 }
30 
31 --->static void __pollwait(struct file *filp, wait_queue_head_t *wait_address, poll_table *p)
32 {
33      struct poll_wqueues *pwq = container_of(p, struct poll_wqueues, pt);
34     struct poll_table_entry *entry = poll_get_entry(pwq); 
35      if (!entry)
36     return;
37     entry->filp = get_file(filp);
38     entry->wait_address = wait_address;
39     entry->key = p->_key;
40      init_waitqueue_func_entry(&entry->wait, pollwake);//唤醒接口
41     entry->wait.private = pwq;
42     add_wait_queue(wait_address, &entry->wait);//将等待队列项添加到等待队列中
43 }
  1 #include <linux/module.h>
  2 #include <linux/fs.h>
  3 #include <linux/platform_device.h>
  4 #include <linux/device.h>
  5 #include <asm/uaccess.h>
  6 #include <linux/irqreturn.h>
  7 #include <linux/interrupt.h>
  8 #include <linux/sched.h>
  9 
 10 int major;
 11 
 12 struct class *cls;
 13 struct device *devs;
 14 struct resource *res_key2;
 15 struct resource *res_key3;
 16 
 17 int key;
 18 wait_queue_head_t keyq;
 19 int flag = 0;
 20 struct timer_list t;
 21 int glo_irqno;
 22 
 23 struct work_struct ws;
 24 
 25 irqreturn_t fs4412_key_handler(int irqno,void *id)
 26 {
 27     glo_irqno = irqno;
 28     mod_timer(&t,jiffies + 30);
 29     return IRQ_HANDLED;
 30 }
 31 
 32 void fs4412_key_timer_handler(unsigned long data)
 33 {
 34     if(glo_irqno == res_key2->start)
 35         key = 2;
 36     if(glo_irqno == res_key3->start)
 37         key = 3;
 38 //    wake_up(&keyq);
 39     wake_up_interruptible(&keyq);
 40     flag = 1;
 41     schedule_work(&ws);
 42 }
 43 
 44 void fs4412_key_work(struct work_struct *work)
 45 {
 46     printk("work schedule
");
 47 }
 48 
 49 int fs4412_key_open(struct inode *inode ,struct file *filp)
 50 {
 51     return 0;
 52 }
 53 
 54 ssize_t fs4412_key_read(struct file *filp,char __user *ubuf,size_t size,loff_t *off)
 55 {
 56     int ret;
 57     
 58 //    wait_event(keyq,flag != 0);
 59     wait_event_interruptible(keyq,flag != 0);
 60     ret = copy_to_user(ubuf,&key,sizeof(key));
 61 
 62     flag = 0;
 63     return sizeof(key);
 64 }
 65 
 66 struct file_operations fops = {
 67     .owner = THIS_MODULE,
 68     .open = fs4412_key_open,
 69     .read = fs4412_key_read,
 70 };
 71 
 72 int fs4412_key_probe(struct platform_device *pdev)
 73 {
 74     int ret;
 75     printk("match ok
");
 76 
 77     major = register_chrdev(0,"key",&fops);
 78     cls = class_create(THIS_MODULE,"key");
 79     devs = device_create(cls,NULL,MKDEV(major,0),NULL,"key");
 80     
 81     res_key2 = platform_get_resource(pdev,IORESOURCE_IRQ,0);//索引interrupts = <>,<>第一个<>
 82     res_key3 = platform_get_resource(pdev,IORESOURCE_IRQ,1);
 83     ret = request_irq(res_key2->start,fs4412_key_handler,IRQF_TRIGGER_FALLING,"key2",NULL);
 84     ret = request_irq(res_key3->start,fs4412_key_handler,IRQF_TRIGGER_FALLING,"key3",NULL);
 85 
 86     init_waitqueue_head(&keyq);
 87 
 88     init_timer(&t);
 89     t.function = fs4412_key_timer_handler;//定时器中断处理函数
 90     add_timer(&t);//让内核认识定时器中断处理函数
 91 
 92     INIT_WORK(&ws,fs4412_key_work);
 93     return 0;
 94 }
 95 
 96 int fs4412_key_remove(struct platform_device *pdev)
 97 {
 98     del_timer(&t);
 99     free_irq(res_key3->start,NULL);
100     free_irq(res_key2->start,NULL);
101     device_destroy(cls,MKDEV(major,0));
102     class_destroy(cls);
103     unregister_chrdev(major,"key");
104     return 0;
105 }
106 
107 struct of_device_id fs4412_dt_tbl[] = {
108     {
109         .compatible = "fs4412,key",
110     },
111     {},
112 };
113 
114 
115 struct platform_driver pdrv = {
116     .driver = {
117         .name = "fs4412-key",
118         .of_match_table = fs4412_dt_tbl,
119     },
120     .probe = fs4412_key_probe,
121     .remove = fs4412_key_remove,
122 };
123 
124 module_platform_driver(pdrv);
125 MODULE_LICENSE("GPL");
工作队列
 1 #include <stdio.h>
 2 #include <sys/types.h>
 3 #include <sys/stat.h>
 4 #include <fcntl.h>
 5 
 6 int main(int argc, const char *argv[])
 7 {
 8     int fd;
 9 
10     fd = open("/dev/key",O_RDWR);
11     if(fd == -1)
12     {
13         perror("open");
14         return -1;
15     }
16     
17     int key;
18     while(1)
19     {
20         read(fd,&key,sizeof(key));
21         printf("key = %d
",key);
22     }
23     return 0;
24 }
应用层代码

异步通知(信号操作): 

 1 #include<stdio.h>
 2 #include<sys/types.h>
 3 #include<sys/stat.h>
 4 #include<fcntl.h>
 5 #include<unistd.h>
 6 #include<errno.h>
 7 #include<stdlib.h>
 8 #include<signal.h>
 9 int fd;
10 char buf[64] = {0};
11 
12 void func(int sig)
13 {
14     read(fd,buf,sizeof(buf));
15     printf("%s
",buf);
16 }
17 
18 int main(int argc, const char *argv[])
19 {
20     
21     char buf1[64] = {0};
22     if(mkfifo("./fifo",0664) == -1)
23     {
24         if(errno == EEXIST)
25         {
26             fd = open("./fifo",O_RDONLY);    
27         }
28         else 
29         {
30             perror("mkfifo");
31             exit(1);
32         }
33     }
34     else 
35     {
36         fd = open("./fifo",O_RDONLY);
37     }
38     
39     signal(SIGIO,func);
40 
41     int flag;
42     flag = fcntl(fd,F_GETFL);
43     flag = flag | O_ASYNC;
44     fcntl(fd,F_SETFL,flag);
45     fcntl(fd,F_SETOWN,getpid());
46     while(1)
47     {
48         printf("hello
");
49         sleep(1);
50     }
51     return 0;
52 }
signal_read.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<errno.h>
 7 #include<stdlib.h>
 8 #include<string.h>
 9 
10 int main(int argc, const char *argv[])
11 {
12     int fd;
13 
14     char buf[64] = {0};
15     if(mkfifo("./fifo",0664) == -1)
16     {
17         if(errno == EEXIST)
18         {
19             fd = open("./fifo",O_WRONLY);    
20         }
21         else 
22         {
23             perror("mkfifo");
24             exit(1);
25         }
26     }
27     else 
28     {
29         fd = open("./fifo",O_WRONLY);
30     }
31 
32     while(1)
33     {
34         fgets(buf,sizeof(buf),stdin);
35         write(fd,buf,strlen(buf) + 1);
36     }
37     return 0;
38 }
signal_write.c
1 应用程序:
2 int flag;
3 fd = open("1.txt",O_RDONLY);//一旦打开文件,内核会创建file结构体,其中有一个f_flags成员,此时f_flags = O_RDONLY
4 flag = fcntl(fd,F_GETFL);//从file结构体中获取O_RDONLY保存到flag变量中
5 flag |= O_ASYNC;//将O_RDONLY | O_ASYNC保存到flag变量中(flag是自定义的变量不是内核的)
6 fctnl(fd,F_SETFL,flag);//将O_RDONLY | O_ASYNC保存到file结构体的f_flags成员中,内核会指定如果使用了O_ASYNC属性则默认会使用SIGIO信号
7 
8 fcntl(fd,F_SETOWN,getpid());//需要注册信号,但是这个函数只指定了给哪个进程注册,谁在给进程注册信号?内核实现注册功能
 1 内核源码分析:
 2 vi arch/arm/include/uapi/asm/unistd.h
 3 
 4 #define __NR_fcntl (__NR_SYSCALL_BASE+ 55)
 5 
 6 --->__SYSCALL(__NR_fcntl, sys_fcntl)
 7 
 8 --->err = do_fcntl(fd, cmd, arg, f.file);
 9 
10 --->case F_GETFL: 
11 err = filp->f_flags;对应了flag = fcntl(fd,F_GETFL);
12     break;
13     case F_SETFL:
14     err = setfl(fd, filp, arg);fctnl(fd,F_SETFL,flag)
15 
16 ---> if (((arg ^ filp->f_flags) & FASYNC) && filp->f_op->fasync) {
17 error = filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0); 
18 }    
19 上层如果执行fctnl(fd,F_SETFL,flag),驱动层可能执行fasync接口,前提是flag |= O_ASYNC;改成flag |= FASYNC;
  1 #include <linux/module.h>
  2 #include <linux/fs.h>
  3 #include <linux/platform_device.h>
  4 #include <linux/device.h>
  5 #include <asm/uaccess.h>
  6 #include <linux/irqreturn.h>
  7 #include <linux/interrupt.h>
  8 #include <linux/sched.h>
  9 #include <linux/poll.h>
 10 
 11 int major;
 12 
 13 struct class *cls;
 14 struct device *devs;
 15 struct resource *res_key2;
 16 struct resource *res_key3;
 17 
 18 int key;
 19 wait_queue_head_t keyq;
 20 int flag = 0;
 21 struct timer_list t;
 22 int glo_irqno;
 23 
 24 struct fasync_struct *fa;
 25 
 26 irqreturn_t fs4412_key_handler(int irqno,void *id)
 27 {
 28     glo_irqno = irqno;
 29     mod_timer(&t,jiffies + 30);
 30     return IRQ_HANDLED;
 31 }
 32 
 33 void fs4412_key_timer_handler(unsigned long data)
 34 {
 35     if(glo_irqno == res_key2->start)
 36         key = 2;
 37     if(glo_irqno == res_key3->start)
 38         key = 3;
 39 //    wake_up(&keyq);
 40     wake_up_interruptible(&keyq);
 41     flag = 1;
 42     
 43     //注册SIGIO信号
 44     kill_fasync(&fa,SIGIO,POLLIN);
 45 }
 46 
 47 int fs4412_key_open(struct inode *inode ,struct file *filp)
 48 {
 49     return 0;
 50 }
 51 
 52 ssize_t fs4412_key_read(struct file *filp,char __user *ubuf,size_t size,loff_t *off)
 53 {
 54     int ret;
 55     
 56 //    wait_event(keyq,flag != 0);
 57     wait_event_interruptible(keyq,flag != 0);
 58     ret = copy_to_user(ubuf,&key,sizeof(key));
 59 
 60     flag = 0;
 61     return sizeof(key);
 62 }
 63 
 64 int fs4412_key_fasync(int fd,struct file *filp,int on)
 65 {
 66     fasync_helper(fd,filp,on,&fa);//为了kill_fasync函数做一些成员赋值
 67     return 0;
 68 }
 69 
 70 struct file_operations fops = {
 71     .owner = THIS_MODULE,
 72     .open = fs4412_key_open,
 73     .read = fs4412_key_read,
 74     .fasync = fs4412_key_fasync,
 75 };
 76 
 77 int fs4412_key_probe(struct platform_device *pdev)
 78 {
 79     int ret;
 80     printk("match ok
");
 81 
 82     major = register_chrdev(0,"key",&fops);
 83     cls = class_create(THIS_MODULE,"key");
 84     devs = device_create(cls,NULL,MKDEV(major,0),NULL,"key");
 85     
 86     res_key2 = platform_get_resource(pdev,IORESOURCE_IRQ,0);//索引interrupts = <>,<>第一个<>
 87     res_key3 = platform_get_resource(pdev,IORESOURCE_IRQ,1);
 88     ret = request_irq(res_key2->start,fs4412_key_handler,IRQF_TRIGGER_FALLING,"key2",NULL);
 89     ret = request_irq(res_key3->start,fs4412_key_handler,IRQF_TRIGGER_FALLING,"key3",NULL);
 90 
 91     init_waitqueue_head(&keyq);
 92 
 93     init_timer(&t);
 94     t.function = fs4412_key_timer_handler;//定时器中断处理函数
 95     add_timer(&t);//让内核认识定时器中断处理函数
 96     return 0;
 97 }
 98 
 99 int fs4412_key_remove(struct platform_device *pdev)
100 {
101     del_timer(&t);
102     free_irq(res_key3->start,NULL);
103     free_irq(res_key2->start,NULL);
104     device_destroy(cls,MKDEV(major,0));
105     class_destroy(cls);
106     unregister_chrdev(major,"key");
107     return 0;
108 }
109 
110 struct of_device_id fs4412_dt_tbl[] = {
111     {
112         .compatible = "fs4412,key",
113     },
114     {
115      
116        },
117 };
118 
119 
120 struct platform_driver pdrv = {
121     .driver = {
122         .name = "fs4412-key",
123         .of_match_table = fs4412_dt_tbl,
124     },
125     .probe = fs4412_key_probe,
126     .remove = fs4412_key_remove,
127 };
128 
129 module_platform_driver(pdrv);
130 MODULE_LICENSE("GPL");
131     
signal_key.c代码
 1 #include <stdio.h>
 2 #include <sys/types.h>
 3 #include <sys/stat.h>
 4 #include <fcntl.h>
 5 #include <signal.h>
 6 
 7 int fd;
 8 int key;
 9 void func(int signo)
10 {
11     if(signo == SIGIO)
12     {
13         read(fd,&key,sizeof(key));
14         printf("key = %d
",key);
15     }
16 }
17 
18 int main(int argc, const char *argv[])
19 {
20     signal(SIGIO,func);
21 
22     fd = open("/dev/key",O_RDWR);
23     if(fd == -1)
24     {
25         perror("open");
26         return -1;
27     }
28     
29     int flag;
30     flag = fcntl(fd,F_GETFL);
31     flag |= FASYNC;
32     fcntl(fd,F_SETFL,flag);//主要功能为了调用驱动中的fasync接口
33     fcntl(fd,F_SETOWN,getpid());
34     
35     while(1)
36     {
37         printf("hello
");
38         sleep(1);
39     }
40     return 0;
41 }
应用层代码

总结:

 1 中断上半部和下半部
 2 将一个中断处理函数分成了2个函数。
 3 
 4 上半部用来处理紧急事件
 5 
 6 紧急事件:
 7 1、直接操作硬件
 8 2、对时间敏感
 9 3、不能被打断的
10 
11 下半部:
12 软中断:
13     open_softirq();
14     raise_softirq();
15 几乎不会使用软中断,内核提供的软中断号都已经被占用了,对应的接口也没有导出符号表。
16 
17 小任务:属于一种特殊的软中断,运行在中断上下文,中断处理函数中不能有延时,只能自旋锁。
18     tasklet_init();//初始化小任务
19     tasklet_schedule();//调度小任务
20     
21 工作队列:运行在进程上下文,可以有延时,可以使用互斥锁,信号量,等待队列。
22     INIT_WORK();
23     schedule_work();
24     
25 什么时候调度中断下半部?
26 一般都是在上半部中断处理函数返回前或者返回后。
27 
28 多路复用:
29 int select(文件描述符最大值+1,读表,写表,异常表,超时);
30 功能:如果文件描述符没有就绪则阻塞。
31      如果文件描述符就绪则唤醒,唤醒后会清除未就绪的文件描述符。
32 
33 vi arch/arm/include/uapi/asm/unistd.h
34 
35 
36 异步通知:当一种事件(按键按下)发生时会在应用层产生一个SIGIO信号,进而捕捉SIGIO信号
37 
38 应用层:
39 
40 signal(SIGIO,func);
41 fd = open();
42 
43 flag = fcntl(fd,F_GETFL);//从file结构体的f_flags成员中获取属性存放给flag
44 flag |= FASYNC;
45 fcntl(fd,F_SETFL,flag);//会和FASYNC进行&,如果成功则会执行驱动中的fasync接口。
46 fcntl(fd,F_SETOWN,getpid());//明确给哪个进程注册SIGIO信号(还没有真正注册)。
47 
48 while(1)
49 {
50     功能;
51 }
52 
53 驱动层:
54 中断处理函数
55 {
56     kill_fasync();
57     //给进程注册信号,但是注册信号时需要使用到一下变量内容,需要准备这些内容
58 }
59 
60 fasync()
61 {
62     fasync_helper();//为kill_fasync的功能实现做准备。
63 }
原文地址:https://www.cnblogs.com/hslixiqian/p/9670835.html