Linux字符设备驱动之异步通知

poll机制可实现有数据的时候就去读,没有数据的时候,如果超过规定一个时间,就表示超时时间。poll机制需要应用程序主动去读,而异步通知并不需要,一旦设备就绪,则主动通知应用程序,应用程序不需要主动查询设备状态,类似于中断的概念,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的,一个进程不必通过任何操作来 等待信号的到达。
在linux中,异步通知是使用信号来实现的,而在linux,大概有30种信号,比如大家熟悉的ctrl+c的sigint信号,进程能够忽略或者捕获除过sigstop和sigkill的全部信号,当信号背捕获以后,有相应的函数来处理它。
实现异步通知的四个要素:
一、应用程序要实现:注册信号处理函数,使用signal函数;
二、谁来发?驱动来发;
三、发给谁?发给应用程序,但应用程序必须告诉驱动pid;
四、怎么发?驱动程序使用kill_fasync函数;
问:应该在驱动的哪里调用kill_fasync函数?
答:kill_fasync函数的作用是,当有数据时去通知应用程序,理所当然的应该在用户终端处理函数里调用。
问:file_operations需要添加什么函数指针成员吗?
答:要的,需要添加fasync函数指针,要实现这个函数指针,幸运的是,这个函数仅仅调用了fasync_helper函数,而且这个函数是内核帮我们实现好了,驱动工程师不用修改,fasync_helper函数的作用是初始化/释放fasync_struct
详细请参考驱动源码
#include #include #include #include #include #include #include #include #include #include //class_create#include //s3c2410_gpf1//#include #include //#include #include //wait_event_interruptible#include //poll#include /* 定义并初始化等待队列头 */static declare_wait_queue_head(button_waitq);static struct class *fifthdrv_class;static struct device *fifthdrv_device;static struct pin_desc { unsigned int pin; unsigned int key_val;};static struct pin_desc pins_desc[4] = { {s3c2410_gpf1, 0x01}, {s3c2410_gpf4, 0x02}, {s3c2410_gpf2, 0x03}, {s3c2410_gpf0, 0x04},};static int ev_press = 0;/* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 *//* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */static unsigned char key_val;int major;static struct fasync_struct *button_fasync;/* 用户中断处理函数 */static irqreturn_t buttons_irq(int irq, void *dev_id){ struct pin_desc *pindesc = (struct pin_desc *)dev_id; unsigned int pinval; pinval = s3c2410_gpio_getpin(pindesc->pin); if(pinval) { /* 松开 */ key_val = 0x80 | (pindesc->key_val); } else { /* 按下 */ key_val = pindesc->key_val; } ev_press = 1;/* 表示中断已经发生 */ wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */ /* 用kill_fasync函数告诉应用程序,有数据可读了 * button_fasync结构体里包含了发给谁(pid指定) * sigio表示要发送的信号类型 * poll_in表示发送的原因(有数据可读了) */ kill_fasync(&button_fasync, sigio, poll_in); return irq_handled;}static int fifth_drv_open(struct inode * inode, struct file * filp){ /* k1 ---- eint1,k2 ---- eint4,k3 ---- eint2,k4 ---- eint0 * 配置gpf1、gpf4、gpf2、gpf0为相应的外部中断引脚 * irqt_bothedge应该改为irq_type_edge_both */ request_irq(irq_eint1, buttons_irq, irq_type_edge_both, k1,&pins_desc[0]); request_irq(irq_eint4, buttons_irq, irq_type_edge_both, k2,&pins_desc[1]); request_irq(irq_eint2, buttons_irq, irq_type_edge_both, k3,&pins_desc[2]); request_irq(irq_eint0, buttons_irq, irq_type_edge_both, k4,&pins_desc[3]); return 0;}static ssize_t fifth_drv_read(struct file *file, char __user *user, size_t size,loff_t *ppos){ if (size != 1) return -einval; /* 当没有按键按下时,休眠。 * 即ev_press = 0; * 当有按键按下时,发生中断,在中断处理函数会唤醒 * 即ev_press = 1; * 唤醒后,接着继续将数据通过copy_to_user函数传递给应用程序 */ wait_event_interruptible(button_waitq, ev_press); copy_to_user(user, &key_val, 1); /* 将ev_press清零 */ ev_press = 0; return 1;}static int fifth_drv_close(struct inode *inode, struct file *file){ free_irq(irq_eint1,&pins_desc[0]); free_irq(irq_eint4,&pins_desc[1]); free_irq(irq_eint2,&pins_desc[2]); free_irq(irq_eint0,&pins_desc[3]); return 0;}static unsigned int fifth_drv_poll(struct file *file, poll_table *wait){ unsigned int mask = 0; /* 该函数,只是将进程挂在button_waitq队列上,而不是立即休眠 */ poll_wait(file, &button_waitq, wait); /* 当没有按键按下时,即不会进入按键中断处理函数,此时ev_press = 0 * 当按键按下时,就会进入按键中断处理函数,此时ev_press被设置为1 */ if(ev_press) { mask |= pollin | pollrdnorm; /* 表示有数据可读 */ } /* 如果有按键按下时,mask |= pollin | pollrdnorm,否则mask = 0 */ return mask;}/* 当应用程序调用了fcntl(fd, f_setfl, oflags | fasync); * 则最终会调用驱动的fasync函数,在这里则是fifth_drv_fasync * fifth_drv_fasync最终又会调用到驱动的fasync_helper函数 * fasync_helper函数的作用是初始化/释放fasync_struct */static int fifth_drv_fasync(int fd, struct file *filp, int on){ return fasync_helper(fd, filp, on, &button_fasync);}/* file operations struct for character device */static const struct file_operations fifth_drv_fops = { .owner = this_module, .open = fifth_drv_open, .read = fifth_drv_read, .release = fifth_drv_close, .poll = fifth_drv_poll, .fasync = fifth_drv_fasync,};/* 驱动入口函数 */static int fifth_drv_init(void){ /* 主设备号设置为0表示由系统自动分配主设备号 */ major = register_chrdev(0, fifth_drv, &fifth_drv_fops); /* 创建fifthdrv类 */ fifthdrv_class = class_create(this_module, fifthdrv); /* 在fifthdrv类下创建buttons设备,供应用程序打开设备*/ fifthdrv_device = device_create(fifthdrv_class, null, mkdev(major, 0), null, buttons); return 0;}/* 驱动出口函数 */static void fifth_drv_exit(void){ unregister_chrdev(major, fifth_drv); device_unregister(fifthdrv_device); //卸载类下的设备 class_destroy(fifthdrv_class); //卸载类}module_init(fifth_drv_init); //用于修饰入口函数module_exit(fifth_drv_exit); //用于修饰出口函数module_author(lwj);module_description(just for demon);module_license(gpl); //遵循gpl协议
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
应用测试程序源码
#include #include #include #include #include //sleep#include #include #include int fd;void mysignal_fun(int signum){ unsigned char key_val; read(fd, &key_val, 1); printf(key_val = 0x%x, key_val);}int main(int argc ,char *argv[]){ int flag; signal(sigio, mysignal_fun); fd = open(/dev/buttons, o_rdwr); if (fd f_owner为对应进程id。 不过此项工作已由内核完成,设备驱动无须处理。
支持f_setfl命令的处理,每当fasync标志改变时,驱动程序中的fasync()函数将得以执行。驱动中应该实现fasync()函数。
在设备资源可获得时,调用kill_fasync()函数激发相应的信号
应用程序:
fcntl(fd, f_setown, getpid()); // 告诉内核,发给谁oflags = fcntl(fd, f_getfl);fcntl(fd, f_setfl, oflags | fasync); // 改变fasync标记,最终会调用到驱动的faync > fasync_helper:初始化/释放fasync_s

如何理解大数据,它定义如何以及它的作用
如何使用F1-20简易编程器进行编程工作
无线AP和路由器二者的区别
三星电子将申请Hologram技术相关专利
清华微电子发布AI芯片 专注于神经网络计算,可用于ai,图像等多种应用
Linux字符设备驱动之异步通知
全球TOP3,中国已掌握最高端服务器主机核心技术
PCB如何设计才能发挥出EMC最全面的效果
中国高端无人机在中国航展上公开亮相
如何判断自己的iPhone12是否存在绿屏问题?
适用于多种电压等级的蓄电池过放电保护电路
Intel遭遇的窘境,更换CEO或许对Intel是好事一件
任天堂新世代游戏机国行版Switch天猫首售销量破万台
iPhone7好基友苹果BeatsX耳机要来了,搭载苹果W1无线芯片
智能灯杆网关设备的功能优势及应用场景介绍
电子制作:打造属于你的WIFI ROBOT
不同的脉冲宽度测量技术优势
时间先后判决器电路图
传感器电路抑制电磁干扰的四种方法
一文让你知道自己的无线遥控器能不能对码——再也不用请教别人了