驱动开发 —— 异步通知
按键驱动方式对比
- 查询:一直读,耗资源
- 中断: 没有超时机制,当没有中断,read函数一直休眠
- poll机制,加入超时机制
以上3种,都是让应用程序主动去读,本节我们学习异步通知,它的作用就是当驱动层有数据时,主动告诉应用程序,然后应用程序再来读, 这样,应用程序就可以干其它的事情,不必一直读。
当有数据到时候,驱动会发送信号(SIGIO)给应用,就可以异步去读写数据,不用主动去读写。
1、应用·——处理信号,读写数据
void catch_signale(int signo) { if(signo == SIGIO) { printf("we got sigal SIGIO"); // 读取数据 read(fd, &event, sizeof(struct key_event)); if(event.code == KEY_ENTER) { if(event.value) { printf("APP__ key enter pressed\n"); }else { printf("APP__ key enter up\n"); } } } } // 1,设置信号处理方法 signal(SIGIO,catch_signale); // 2,将当前进程设置成SIGIO的属主进程 fcntl(fd, F_SETOWN, getpid()); // 3,将io模式设置成异步模式 int flags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, flags | FASYNC );
2、示例:
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/of.h> 4 #include <linux/of_irq.h> 5 #include <linux/interrupt.h> 6 #include <linux/slab.h> 7 #include <linux/fs.h> 8 #include <linux/device.h> 9 #include <linux/kdev_t.h> 10 #include <linux/err.h> 11 #include <linux/device.h> 12 #include <asm/io.h> 13 #include <asm/uaccess.h> 14 #include <linux/wait.h> 15 #include <linux/sched.h> 16 #include <linux/poll.h> 17 18 19 #define GPXCON_REG 0X11000C20 //不可以从数据寄存器开始映射,要配置寄存器 20 #define KEY_ENTER 28 21 22 //0、设计一个描述按键的数据的对象 23 struct key_event{ 24 int code; //按键类型:home,esc,enter 25 int value; //表状态,按下,松开 26 }; 27 28 //1、设计一个全局对象——— 描述key的信息 29 struct key_desc{ 30 unsigned int dev_major; 31 int irqno; //中断号 32 struct class *cls; 33 struct device *dev; 34 void *reg_base; 35 struct key_event event; 36 wait_queue_head_t wq_head; 37 int key_state; //表示是否有数据 38 struct fasync_struct *faysnc; 39 }; 40 41 struct key_desc *key_dev; 42 43 44 irqreturn_t key_irq_handler(int irqno, void *devid) 45 { 46 printk("----------%s---------",__FUNCTION__); 47 48 int value; 49 //读取按键状态 50 value = readl(key_dev->reg_base + 4) & (0x01<<2); 51 52 if(value){ 53 printk("key3 up\n"); 54 key_dev->event.code = KEY_ENTER; 55 key_dev->event.value = 0; 56 }else{ 57 printk("key3 down\n"); 58 key_dev->event.code = KEY_ENTER; 59 key_dev->event.value = 1; 60 } 61 62 //表示有数据,唤醒等待队列中的等待项 63 wake_up_interruptible(&key_dev->wq_head); 64 65 //同时设置标志位,表示有数据 66 key_dev->key_state = 1; 67 68 //发送信号 69 kill_fasync(&key_dev->faysnc, SIGIO, POLLIN); 70 71 return IRQ_HANDLED; 72 } 73 74 75 //获取中断号 76 int get_irqno_from_node(void) 77 { 78 int irqno; 79 //获取设备树中的节点 80 struct device_node *np = of_find_node_by_path("/key_int_node"); 81 if(np){ 82 printk("find node success\n"); 83 }else{ 84 printk("find node failed\n"); 85 } 86 87 //通过节点去获取中断号 88 irqno = irq_of_parse_and_map(np, 0); 89 printk("iqrno = %d",key_dev->irqno); 90 91 return irqno; 92 } 93 94 ssize_t key_drv_read (struct file * filp, char __user * buf, size_t count, loff_t * fops) 95 { 96 //printk("----------%s---------",__FUNCTION__); 97 int ret; 98 99 //在没有数据时,进行休眠 100 //key_state在zalloc初始化空间后,为0,则阻塞 101 wait_event_interruptible(key_dev->wq_head, key_dev->key_state); 102 103 ret = copy_to_user(buf, &key_dev->event, count); 104 if(ret > 0) 105 { 106 printk("copy_to_user error\n"); 107 return -EFAULT; 108 } 109 110 111 //传递给用户数据后,将数据清除,否则APP每次读都是第一次的数据 112 memset(&key_dev->event, 0, sizeof(key_dev->event)); 113 key_dev->key_state = 0; 114 115 return count; 116 } 117 118 ssize_t key_drv_write (struct file *filp, const char __user * buf, size_t count, loff_t * fops) 119 { 120 printk("----------%s---------",__FUNCTION__); 121 return 0; 122 } 123 124 int key_drv_open (struct inode * inode, struct file *filp) 125 { 126 printk("----------%s---------",__FUNCTION__); 127 return 0; 128 } 129 130 int key_drv_close (struct inode *inode, struct file *filp) 131 { 132 printk("----------%s---------",__FUNCTION__); 133 return 0; 134 } 135 136 unsigned int key_drv_poll (struct file * filp, struct poll_table_struct *pts) 137 { 138 //返回一个mask值 139 unsigned int mask = 0; 140 141 //调用poll_wait,将当前的等待队列注册到系统中 142 poll_wait(filp, &key_dev->wq_head, pts); 143 //1、当没有数据的时候返回0 144 if(!key_dev->key_state) 145 { 146 mask = 0; 147 } 148 //2、当有数据的时候返回POLLIN 149 if(key_dev->key_state) 150 mask |= POLLIN; 151 152 return mask; 153 } 154 155 int key_drv_fasync (int fd, struct file *filp, int on) 156 { 157 //只需要调用一个函数记录信号该发送给谁 158 return fasync_helper(fd, filp, on, &key_dev->faysnc); 159 } 160 161 162 const struct file_operations key_fops = { 163 .open = key_drv_open, 164 .read = key_drv_read, 165 .write = key_drv_write, 166 .release = key_drv_close, 167 .poll = key_drv_poll, 168 .fasync = key_drv_fasync, 169 170 }; 171 172 173 174 static int __init key_drv_init(void) 175 { 176 //演示如何获取到中断号 177 int ret; 178 179 //1、设定全局设备对象并分配空间 180 key_dev = kzalloc(sizeof(struct key_desc), GFP_KERNEL); //GFP_KERNEL表正常分配内存 181 //kzalloc相比于kmalloc,不仅分配连续空间,还会将内存初始化清零 182 183 //2、动态申请设备号 184 key_dev->dev_major = register_chrdev(0, "key_drv", &key_fops); 185 186 //3、创建设备节点文件 187 key_dev->cls = class_create(THIS_MODULE, "key_cls"); 188 key_dev->dev = device_create(key_dev->cls, NULL, MKDEV(key_dev->dev_major, 0), NULL, "key0"); 189 190 //4、硬件初始化 -- 地址映射或中断申请 191 192 key_dev->reg_base = ioremap(GPXCON_REG,8); 193 194 key_dev->irqno = get_irqno_from_node(); 195 196 ret = request_irq(key_dev->irqno, key_irq_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, 197 "key3_eint10", NULL); 198 if(ret != 0) 199 { 200 printk("request_irq error\n"); 201 return ret; 202 } 203 204 //初始化等待队列头 205 init_waitqueue_head(&key_dev->wq_head); //wait_queue_head_t *q 206 207 208 209 return 0; 210 } 211 212 static void __exit key_drv_exit(void) 213 { 214 iounmap(GPXCON_REG); 215 free_irq(key_dev->irqno, NULL); //free_irq与request_irq的最后一个参数一致 216 device_destroy(key_dev->cls, MKDEV(key_dev->dev_major, 0)); 217 class_destroy(key_dev->cls); 218 unregister_chrdev(key_dev->dev_major, "key_drv"); 219 kfree(key_dev); 220 } 221 222 223 224 module_init(key_drv_init); 225 module_exit(key_drv_exit); 226 227 MODULE_LICENSE("GPL");
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 #include <unistd.h> 5 #include <sys/types.h> 6 #include <sys/stat.h> 7 #include <fcntl.h> 8 #include <poll.h> 9 #include <signal.h> 10 11 12 #define KEY_ENTER 28 13 14 //0、设计一个描述按键的数据的对象 15 struct key_event{ 16 int code; //按键类型:home,esc,enter 17 int value; //表状态,按下,松开 18 }; 19 20 static int fd; 21 static struct key_event event; 22 23 24 25 void catch_signale(int signo) 26 { 27 if(signo == SIGIO) 28 { 29 printf("I Got A Signal\n"); 30 //读取数据 31 read(fd, &event, sizeof(struct key_event)); //每次读必有数据 32 if(event.code == KEY_ENTER) 33 { 34 if(event.value) 35 { 36 printf("APP__ key enter down\n"); 37 }else{ 38 39 printf("APP__ key enter up\n"); 40 } 41 } 42 43 } 44 } 45 46 int main(int argc, char *argv[]) 47 { 48 49 int ret; 50 fd = open("/dev/key0", O_RDWR); 51 if(fd < 0) 52 { 53 perror("open"); 54 exit(1); 55 } 56 57 //1、设置信号处理方法 58 signal(SIGIO, catch_signale); // 59 60 //2、将当前进程设置成SIGIO的属主进程 61 fcntl(fd, F_SETOWN, getpid()); 62 63 //3、将IO模式设置成异步模式 64 int flags = fcntl(fd, F_GETFL); //获取信号 65 fcntl(fd, F_SETFL, flags | FASYNC); //设置信号为FASYNC 66 67 68 while(1) 69 { 70 //设置异步通知后,APP可以去做其他事了 71 printf("I am waiting .....\n"); 72 sleep(1); 73 } 74 75 76 close(fd); 77 78 return 0; 79 }
3、中断下半部
中断处理的两个原则:
1、中断处理不能嵌套:
中断嵌套突然暴发,那么栈将越来越大,栈终将耗尽。在Linux系统上中断无法嵌套:即当前中断A没处理完之前,不会响应另一个中断B(即使它的优先级更高)。
2、中断处理越快越好:
通常,我们希望中断处理的时间(中断上半部)越短越好,例如。进程正在访问临界资源,此时中断来了,前去处理,但处理时间太长。其他需要访问该临界资源的线程就会被阻塞,导致饥饿。但是,有些时候中断处理的时间就是比较长,例如,网卡接收数据的中断处理中,会先从硬件读取数据,拆包,再往上层传送,若数据量很大,那么处理过程就会很长。
并且进程调度是依靠定时器中断实现的,由于原则1,在中断的处理过程中,该CPU是不能进行进程调度的,所以中断的处理要越快越好,尽早让其他中断能被处理。
当中断处理时间没有办法减少,却要求缩短中断处理时间,该如何处理?由此引入中断下半部的概念,就是将一些耗时的操作延后执行。例如,网卡处理接收数据时,会先将耗时的数据放在全局数据区,延后处理,网卡进程先退出中断处理,返回去继续执行。
如果中断处理的时间够短,可以不用下半部。先看一下中断下半部的实现方法:
1,softirq: 处理比较快,但是内核级别的机制,需要修改整个内核源码,不推荐也不常用 2,tasklet: 内部实现实际调用了softirq a.在内核线程中以链表方式组织 b.运行在中断上下文当中,不可执行休眠的函数 3, workqueue: 工作队列 a.在内核线程中已队列方式组织 b.运行在进程上下文当中,可执行休眠的函数
引用:简单地说,一般的驱动程序的编写者需要做两个选择。 首先,你是不是需要一个可调度的实体来执行需要推后完成的工作――从根本上来说,有休眠的需要吗?要是有,工作队列就是你的惟一选择。 否则最好用tasklet。要是必须专注于性能的提高,那么就考虑softirq。
(中断下半部实现详见:tasklet与workqueue的区别及底层实现区别)
4、中断下半部实现方法 —— tasklet
1 struct tasklet_struct 2 { 3 struct tasklet_struct *next; 4 unsigned long state; 5 atomic_t count; 6 void (*func)(unsigned long); // 下半部的实现逻辑 7 unsigned long data; // 传递给func 8 }; 9 10 a, 初始化(在驱动模块入口,即init中初始化,中断申请之前) 11 struct tasklet_struct mytasklet; 12 13 tasklet_init(struct tasklet_struct * t, void(* func)(unsigned long), unsigned long data) 14 15 例子: 16 void key_tasklet_half_irq(unsigned long data) 17 { 18 // 表示有数据,需要去唤醒整个进程/等待队列 19 wake_up_interruptible(&key_dev->wq_head); 20 //同时设置标志位 21 key_dev->key_state = 1; 22 23 //发送信号 24 kill_fasync(&key_dev->faysnc, SIGIO, POLLIN); 25 } 26 27 tasklet_init(&key_dev->mytasklet, key_tasklet_half_irq, 45); 28 29 30 b,在上半部中放入到内核线程中--启动 31 // 启动下半步 32 tasklet_schedule(&key_dev->mytasklet); 33 34 c,模块卸载的时候: 35 tasklet_kill(&key_dev->mytasklet);
测试代码:
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/of.h> 4 #include <linux/of_irq.h> 5 #include <linux/interrupt.h> 6 #include <linux/slab.h> 7 #include <linux/fs.h> 8 #include <linux/device.h> 9 #include <linux/kdev_t.h> 10 #include <linux/err.h> 11 #include <linux/device.h> 12 #include <asm/io.h> 13 #include <asm/uaccess.h> 14 #include <linux/wait.h> 15 #include <linux/sched.h> 16 #include <linux/poll.h> 17 18 19 #define GPXCON_REG 0X11000C20 //不可以从数据寄存器开始映射,要配置寄存器 20 #define KEY_ENTER 28 21 22 //0、设计一个描述按键的数据的对象 23 struct key_event{ 24 int code; //按键类型:home,esc,enter 25 int value; //表状态,按下,松开 26 }; 27 28 //1、设计一个全局对象——— 描述key的信息 29 struct key_desc{ 30 unsigned int dev_major; 31 int irqno; //中断号 32 struct class *cls; 33 struct device *dev; 34 void *reg_base; 35 struct key_event event; 36 wait_queue_head_t wq_head; 37 int key_state; //表示是否有数据 38 struct fasync_struct *faysnc; 39 struct tasklet_struct mytasklet; 40 }; 41 42 struct key_desc *key_dev; 43 44 void key_tasklet_half_irq(unsigned long data) 45 { 46 printk("-------------%s-----------\n",__FUNCTION__); 47 //表示有数据,唤醒等待队列中的等待项 48 wake_up_interruptible(&key_dev->wq_head); 49 50 //同时设置标志位,表示有数据 51 key_dev->key_state = 1; 52 53 //发送信号 54 kill_fasync(&key_dev->faysnc, SIGIO, POLLIN); 55 } 56 57 irqreturn_t key_irq_handler(int irqno, void *devid) 58 { 59 printk("--------------%s-----------\n",__FUNCTION__); 60 61 int value; 62 //读取按键状态 63 value = readl(key_dev->reg_base + 4) & (0x01<<2); 64 65 if(value){ 66 printk("key3 up\n"); 67 key_dev->event.code = KEY_ENTER; 68 key_dev->event.value = 0; 69 }else{ 70 printk("key3 down\n"); 71 key_dev->event.code = KEY_ENTER; 72 key_dev->event.value = 1; 73 } 74 75 76 //将下半部放到内核线程中启动 77 tasklet_schedule(&key_dev->mytasklet); 78 79 return IRQ_HANDLED; 80 } 81 82 83 //获取中断号 84 int get_irqno_from_node(void) 85 { 86 int irqno; 87 //获取设备树中的节点 88 struct device_node *np = of_find_node_by_path("/key_int_node"); 89 if(np){ 90 printk("find node success\n"); 91 }else{ 92 printk("find node failed\n"); 93 } 94 95 //通过节点去获取中断号 96 irqno = irq_of_parse_and_map(np, 0); 97 printk("iqrno = %d",key_dev->irqno); 98 99 return irqno; 100 } 101 102 ssize_t key_drv_read (struct file * filp, char __user * buf, size_t count, loff_t * fops) 103 { 104 //printk("----------%s---------",__FUNCTION__); 105 int ret; 106 107 //在没有数据时,进行休眠 108 //key_state在zalloc初始化空间后,为0,则阻塞 109 wait_event_interruptible(key_dev->wq_head, key_dev->key_state); 110 111 ret = copy_to_user(buf, &key_dev->event, count); 112 if(ret > 0) 113 { 114 printk("copy_to_user error\n"); 115 return -EFAULT; 116 } 117 118 119 //传递给用户数据后,将数据清除,否则APP每次读都是第一次的数据 120 memset(&key_dev->event, 0, sizeof(key_dev->event)); 121 key_dev->key_state = 0; 122 123 return count; 124 } 125 126 ssize_t key_drv_write (struct file *filp, const char __user * buf, size_t count, loff_t * fops) 127 { 128 printk("----------%s---------",__FUNCTION__); 129 return 0; 130 } 131 132 int key_drv_open (struct inode * inode, struct file *filp) 133 { 134 printk("----------%s---------",__FUNCTION__); 135 return 0; 136 } 137 138 int key_drv_close (struct inode *inode, struct file *filp) 139 { 140 printk("----------%s---------",__FUNCTION__); 141 return 0; 142 } 143 144 unsigned int key_drv_poll (struct file * filp, struct poll_table_struct *pts) 145 { 146 //返回一个mask值 147 unsigned int mask = 0; 148 149 //调用poll_wait,将当前的等待队列注册到系统中 150 poll_wait(filp, &key_dev->wq_head, pts); 151 //1、当没有数据的时候返回0 152 if(!key_dev->key_state) 153 { 154 mask = 0; 155 } 156 //2、当有数据的时候返回POLLIN 157 if(key_dev->key_state) 158 mask |= POLLIN; 159 160 return mask; 161 } 162 163 int key_drv_fasync (int fd, struct file *filp, int on) 164 { 165 //只需要调用一个函数记录信号该发送给谁 166 return fasync_helper(fd, filp, on, &key_dev->faysnc); 167 } 168 169 170 const struct file_operations key_fops = { 171 .open = key_drv_open, 172 .read = key_drv_read, 173 .write = key_drv_write, 174 .release = key_drv_close, 175 .poll = key_drv_poll, 176 .fasync = key_drv_fasync, 177 178 }; 179 180 181 182 static int __init key_drv_init(void) 183 { 184 //演示如何获取到中断号 185 int ret; 186 187 188 //1、设定全局设备对象并分配空间 189 key_dev = kzalloc(sizeof(struct key_desc), GFP_KERNEL); //GFP_KERNEL表正常分配内存 190 //kzalloc相比于kmalloc,不仅分配连续空间,还会将内存初始化清零 191 192 //2、动态申请设备号 193 key_dev->dev_major = register_chrdev(0, "key_drv", &key_fops); 194 195 //3、创建设备节点文件 196 key_dev->cls = class_create(THIS_MODULE, "key_cls"); 197 key_dev->dev = device_create(key_dev->cls, NULL, MKDEV(key_dev->dev_major, 0), NULL, "key0"); 198 199 //4、硬件初始化 -- 地址映射或中断申请 200 201 key_dev->reg_base = ioremap(GPXCON_REG,8); 202 203 key_dev->irqno = get_irqno_from_node(); 204 205 ret = request_irq(key_dev->irqno, key_irq_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, 206 "key3_eint10", NULL); 207 if(ret != 0) 208 { 209 printk("request_irq error\n"); 210 return ret; 211 } 212 213 //初始化等待队列头 214 init_waitqueue_head(&key_dev->wq_head); //wait_queue_head_t *q 215 216 //初始化tasklet 217 tasklet_init(&key_dev->mytasklet, key_tasklet_half_irq, 66); 218 219 return 0; 220 } 221 222 static void __exit key_drv_exit(void) 223 { 224 tasklet_kill(&key_dev->mytasklet); 225 iounmap(GPXCON_REG); 226 free_irq(key_dev->irqno, NULL); //free_irq与request_irq的最后一个参数一致 227 device_destroy(key_dev->cls, MKDEV(key_dev->dev_major, 0)); 228 class_destroy(key_dev->cls); 229 unregister_chrdev(key_dev->dev_major, "key_drv"); 230 kfree(key_dev); 231 } 232 233 234 235 module_init(key_drv_init); 236 module_exit(key_drv_exit); 237 238 MODULE_LICENSE("GPL");
5、工作队列实现下半部
在中断下半部的执行过程中,虽然是开中断的,期间可以处理各类中断。但是毕竟整个中断的处理还没走完,这期间APP是无法执行的。
假设下半部要执行1、2分钟,在这1、2分钟里APP都是无法响应的。
所以,如果中断要做的事情实在太耗时,那就不能用中断下半部来做,而应该用内核线程来做:在中断上半部唤醒内核线程。内核线程和APP都一样竞争执行,APP有机会执行,系统不会卡顿。
这个内核线程是系统帮我们创建的,一般是kworker线程,内核中有很多这样的线程:
kworker线程要去“工作队列”(work queue)上取出一个一个“工作”(work),来执行它里面的函数
typedef void (*work_func_t)(struct work_struct *work); struct work_struct { atomic_long_t data; struct list_head entry; work_func_t func; }; a, 初始化 void work_irq_half(struct work_struct *work) { printk("-------%s-------------\n", __FUNCTION__); // 表示有数据,需要去唤醒整个进程/等待队列 wake_up_interruptible(&key_dev->wq_head); //同时设置标志位 key_dev->key_state = 1; //发送信号 kill_fasync(&key_dev->faysnc, SIGIO, POLLIN); } struct work_struct mywork; INIT_WORK(struct work_struct *work, work_func_t func); b, 在上半部中放入到内核线程中--启动 schedule_work(&key_dev->mywork);
测试代码:
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/of.h> 4 #include <linux/of_irq.h> 5 #include <linux/interrupt.h> 6 #include <linux/slab.h> 7 #include <linux/fs.h> 8 #include <linux/device.h> 9 #include <linux/kdev_t.h> 10 #include <linux/err.h> 11 #include <linux/device.h> 12 #include <asm/io.h> 13 #include <asm/uaccess.h> 14 #include <linux/wait.h> 15 #include <linux/sched.h> 16 #include <linux/poll.h> 17 18 19 #define GPXCON_REG 0X11000C20 //不可以从数据寄存器开始映射,要配置寄存器 20 #define KEY_ENTER 28 21 22 //0、设计一个描述按键的数据的对象 23 struct key_event{ 24 int code; //按键类型:home,esc,enter 25 int value; //表状态,按下,松开 26 }; 27 28 //1、设计一个全局对象——— 描述key的信息 29 struct key_desc{ 30 unsigned int dev_major; 31 int irqno; //中断号 32 struct class *cls; 33 struct device *dev; 34 void *reg_base; 35 struct key_event event; 36 wait_queue_head_t wq_head; 37 int key_state; //表示是否有数据 38 struct fasync_struct *faysnc; 39 struct work_struct mywork; 40 }; 41 42 struct key_desc *key_dev; 43 44 void key_work_half_irq(struct work_struct *work) 45 { 46 printk("-------------%s-----------\n",__FUNCTION__); 47 //表示有数据,唤醒等待队列中的等待项 48 wake_up_interruptible(&key_dev->wq_head); 49 50 //同时设置标志位,表示有数据 51 key_dev->key_state = 1; 52 53 //发送信号 54 kill_fasync(&key_dev->faysnc, SIGIO, POLLIN); 55 } 56 57 irqreturn_t key_irq_handler(int irqno, void *devid) 58 { 59 printk("--------------%s-----------\n",__FUNCTION__); 60 61 int value; 62 //读取按键状态 63 value = readl(key_dev->reg_base + 4) & (0x01<<2); 64 65 if(value){ 66 printk("key3 up\n"); 67 key_dev->event.code = KEY_ENTER; 68 key_dev->event.value = 0; 69 }else{ 70 printk("key3 down\n"); 71 key_dev->event.code = KEY_ENTER; 72 key_dev->event.value = 1; 73 } 74 75 76 //将下半部放到内核线程中启动 77 schedule_work(&key_dev->mywork); 78 79 return IRQ_HANDLED; 80 } 81 82 83 //获取中断号 84 int get_irqno_from_node(void) 85 { 86 int irqno; 87 //获取设备树中的节点 88 struct device_node *np = of_find_node_by_path("/key_int_node"); 89 if(np){ 90 printk("find node success\n"); 91 }else{ 92 printk("find node failed\n"); 93 } 94 95 //通过节点去获取中断号 96 irqno = irq_of_parse_and_map(np, 0); 97 printk("iqrno = %d",key_dev->irqno); 98 99 return irqno; 100 } 101 102 ssize_t key_drv_read (struct file * filp, char __user * buf, size_t count, loff_t * fops) 103 { 104 //printk("----------%s---------",__FUNCTION__); 105 int ret; 106 107 //在没有数据时,进行休眠 108 //key_state在zalloc初始化空间后,为0,则阻塞 109 wait_event_interruptible(key_dev->wq_head, key_dev->key_state); 110 111 ret = copy_to_user(buf, &key_dev->event, count); 112 if(ret > 0) 113 { 114 printk("copy_to_user error\n"); 115 return -EFAULT; 116 } 117 118 119 //传递给用户数据后,将数据清除,否则APP每次读都是第一次的数据 120 memset(&key_dev->event, 0, sizeof(key_dev->event)); 121 key_dev->key_state = 0; 122 123 return count; 124 } 125 126 ssize_t key_drv_write (struct file *filp, const char __user * buf, size_t count, loff_t * fops) 127 { 128 printk("----------%s---------",__FUNCTION__); 129 return 0; 130 } 131 132 int key_drv_open (struct inode * inode, struct file *filp) 133 { 134 printk("----------%s---------",__FUNCTION__); 135 return 0; 136 } 137 138 int key_drv_close (struct inode *inode, struct file *filp) 139 { 140 printk("----------%s---------",__FUNCTION__); 141 return 0; 142 } 143 144 unsigned int key_drv_poll (struct file * filp, struct poll_table_struct *pts) 145 { 146 //返回一个mask值 147 unsigned int mask = 0; 148 149 //调用poll_wait,将当前的等待队列注册到系统中 150 poll_wait(filp, &key_dev->wq_head, pts); 151 //1、当没有数据的时候返回0 152 if(!key_dev->key_state) 153 { 154 mask = 0; 155 } 156 //2、当有数据的时候返回POLLIN 157 if(key_dev->key_state) 158 mask |= POLLIN; 159 160 return mask; 161 } 162 163 int key_drv_fasync (int fd, struct file *filp, int on) 164 { 165 //只需要调用一个函数记录信号该发送给谁 166 return fasync_helper(fd, filp, on, &key_dev->faysnc); 167 } 168 169 170 const struct file_operations key_fops = { 171 .open = key_drv_open, 172 .read = key_drv_read, 173 .write = key_drv_write, 174 .release = key_drv_close, 175 .poll = key_drv_poll, 176 .fasync = key_drv_fasync, 177 178 }; 179 180 181 182 static int __init key_drv_init(void) 183 { 184 //演示如何获取到中断号 185 int ret; 186 187 188 //1、设定全局设备对象并分配空间 189 key_dev = kzalloc(sizeof(struct key_desc), GFP_KERNEL); //GFP_KERNEL表正常分配内存 190 //kzalloc相比于kmalloc,不仅分配连续空间,还会将内存初始化清零 191 192 //2、动态申请设备号 193 key_dev->dev_major = register_chrdev(0, "key_drv", &key_fops); 194 195 //3、创建设备节点文件 196 key_dev->cls = class_create(THIS_MODULE, "key_cls"); 197 key_dev->dev = device_create(key_dev->cls, NULL, MKDEV(key_dev->dev_major, 0), NULL, "key0"); 198 199 //4、硬件初始化 -- 地址映射或中断申请 200 201 key_dev->reg_base = ioremap(GPXCON_REG,8); 202 203 key_dev->irqno = get_irqno_from_node(); 204 205 ret = request_irq(key_dev->irqno, key_irq_handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, 206 "key3_eint10", NULL); 207 if(ret != 0) 208 { 209 printk("request_irq error\n"); 210 return ret; 211 } 212 213 //初始化等待队列头 214 init_waitqueue_head(&key_dev->wq_head); //wait_queue_head_t *q 215 216 //初始化workqueue 217 INIT_WORK(&key_dev->mywork, key_work_half_irq); 218 219 220 return 0; 221 } 222 223 static void __exit key_drv_exit(void) 224 { 225 free_irq(key_dev->irqno, NULL); //free_irq与request_irq的最后一个参数一致 226 iounmap(key_dev->reg_base); 227 device_destroy(key_dev->cls, MKDEV(key_dev->dev_major, 0)); 228 class_destroy(key_dev->cls); 229 unregister_chrdev(key_dev->dev_major, "key_drv"); 230 kfree(key_dev); 231 } 232 233 234 235 module_init(key_drv_init); 236 module_exit(key_drv_exit); 237 238 MODULE_LICENSE("GPL");
推荐文章:https://mp.weixin.qq.com/s/Qm1p8GFQbIIl0bqZLJ76sQ