POLL笔记深入
参考韦东山:
poll函数执行流程
poll函数执行流程:
函数执行流程如上图①~⑧所示,重点从③开始看。假设一开始无按键数据:
- ③ APP调用poll之后,进入内核态;
- ④ 导致驱动程序的drv_poll被调用:
- 注意,drv_poll要把自己这个线程挂入等待队列wq中;假设不放入队列里,那以后- 发生中断时,中断服务程序去哪里找到你嘛?
- drv_poll还会判断一下:有没有数据啊?返回这个状态。
- ⑤ 假设当前没有数据,则休眠一会;
- ⑥ 在休眠过程中,按下了按键,发生了中断:
- 在中断服务程序里记录了按键值,并且从wq中把线程唤醒了。
- ⑦ 线程从休眠中被唤醒,继续执行for循环,再次调用drv_poll:
- drv_poll返回数据状态
- ⑧ 哦,你有数据,那从内核态返回到应用态吧
⑨ APP调用read函数读数据
- 如果一直没有数据,调用流程也是类似的,重点从③开始看,如下:
- ③ APP调用poll之后,进入内核态;
- ④ 导致驱动程序的drv_poll被调用:
- 注意,drv_poll要把自己这个线程挂入等待队列wq中;假设不放入队列里,那以后发生中断时,中断服务程序去哪里找到你嘛?
- drv_poll还会判断一下:有没有数据啊?返回这个状态。
- ⑤ 假设当前没有数据,则休眠一会;
- ⑥ 在休眠过程中,一直没有按下了按键,超时时间到:内核把这个线程唤醒;
- ⑦ 线程从休眠中被唤醒,继续执行for循环,再次调用drv_poll:
- drv_poll返回数据状态
- ⑧ 哦,你还是没有数据,但是超时时间到了,那从内核态返回到应用态吧
- ⑨ APP不能调用read函数读数据
注意几点:
① drv_poll要把线程挂入队列wq,但是并不是在drv_poll中进入休眠,而是在调用drv_poll之后休眠
② drv_poll要返回数据状态
③ APP调用一次poll,有可能会导致drv_poll被调用2次
④ 线程被唤醒的原因有2:中断发生了去队列wq中把它唤醒,超时时间到了内核把它唤醒
⑤ APP要判断poll返回的原因:有数据,还是超时。有数据时再去调用read函数。
驱动
使用poll机制时,驱动程序的核心就是提供对应的drv_poll函数。
在drv_poll函数中要做2件事:
① 把当前线程挂入队列wq:poll_wait
APP调用一次poll,可能导致drv_poll被调用2次,但是我们并不需要把当前线程挂入队列2次。
可以使用内核的函数poll_wait把线程挂入队列,如果线程已经在队列里了,它就不会再次挂入。
② 返回设备状态:
APP调用poll函数时,有可能是查询“有没有数据可以读”:POLLIN,也有可能是查询“你有没有空间给我写数据”:POLLOUT。
所以drv_poll要返回自己的当前状态:(POLLIN | POLLRDNORM) 或 (POLLOUT | POLLWRNORM)。
POLLRDNORM等同于POLLIN,为了兼容某些APP把它们一起返回。
POLLWRNORM等同于POLLOUT ,为了兼容某些APP把它们一起返回。
完整代码
只需要看static unsigned int gpio_key_drv_poll(struct file *fp, poll_table * wait)
这一部分即可
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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
struct gpio_key{
int gpio;
struct gpio_desc *gpiod;
int flag;
int irq;
} ;
static struct gpio_key *gpio_keys_100ask;
/* 主设备号 */
static int major = 0;
static struct class *gpio_key_class;
/* 环形缓冲区 */
static int g_keys[BUF_LEN];
static int r, w;
static int is_key_buf_empty(void)
{
return (r == w);
}
static int is_key_buf_full(void)
{
return (r == NEXT_POS(w));
}
static void put_key(int key)
{
if (!is_key_buf_full())
{
g_keys[w] = key;
w = NEXT_POS(w);
}
}
static int get_key(void)
{
int key = 0;
if (!is_key_buf_empty())
{
key = g_keys[r];
r = NEXT_POS(r);
}
return key;
}
static DECLARE_WAIT_QUEUE_HEAD(gpio_key_wait);
/* 实现对应的open/read/write等函数,填入file_operations结构体 */
static ssize_t gpio_key_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
//printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
int err;
int key;
wait_event_interruptible(gpio_key_wait, !is_key_buf_empty());
key = get_key();
err = copy_to_user(buf, &key, 4);
return 4;
}
static unsigned int gpio_key_drv_poll(struct file *fp, poll_table * wait)
{
//调用一次poll可能会调用两次drv_poll
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
//执行poll_wait并不会休眠
poll_wait(fp, &gpio_key_wait, wait);
return is_key_buf_empty() ? 0 : POLLIN | POLLRDNORM;
}
/* 定义自己的file_operations结构体 */
static struct file_operations gpio_key_drv = {
.owner = THIS_MODULE,
.read = gpio_key_drv_read,
.poll = gpio_key_drv_poll,
};
static irqreturn_t gpio_key_isr(int irq, void *dev_id)
{
struct gpio_key *gpio_key = dev_id;
int val;
int key;
val = gpiod_get_value(gpio_key->gpiod);
printk("key %d %d\n", gpio_key->gpio, val);
key = (gpio_key->gpio << 8) | val;
put_key(key);
wake_up_interruptible(&gpio_key_wait);
return IRQ_HANDLED;
}
/* 1. 从platform_device获得GPIO
* 2. gpio=>irq
* 3. request_irq
*/
static int gpio_key_probe(struct platform_device *pdev)
{
int err;
struct device_node *node = pdev->dev.of_node;
int count;
int i;
enum of_gpio_flags flag;
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
count = of_gpio_count(node);
if (!count)
{
printk("%s %s line %d, there isn't any gpio available\n", __FILE__, __FUNCTION__, __LINE__);
return -1;
}
gpio_keys_100ask = kzalloc(sizeof(struct gpio_key) * count, GFP_KERNEL);
for (i = 0; i < count; i++)
{
gpio_keys_100ask[i].gpio = of_get_gpio_flags(node, i, &flag);
if (gpio_keys_100ask[i].gpio < 0)
{
printk("%s %s line %d, of_get_gpio_flags fail\n", __FILE__, __FUNCTION__, __LINE__);
return -1;
}
gpio_keys_100ask[i].gpiod = gpio_to_desc(gpio_keys_100ask[i].gpio);
gpio_keys_100ask[i].flag = flag & OF_GPIO_ACTIVE_LOW;
gpio_keys_100ask[i].irq = gpio_to_irq(gpio_keys_100ask[i].gpio);
}
for (i = 0; i < count; i++)
{
err = request_irq(gpio_keys_100ask[i].irq, gpio_key_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "100ask_gpio_key", &gpio_keys_100ask[i]);
}
/* 注册file_operations */
major = register_chrdev(0, "100ask_gpio_key", &gpio_key_drv); /* /dev/gpio_key */
gpio_key_class = class_create(THIS_MODULE, "100ask_gpio_key_class");
if (IS_ERR(gpio_key_class)) {
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
unregister_chrdev(major, "100ask_gpio_key");
return PTR_ERR(gpio_key_class);
}
device_create(gpio_key_class, NULL, MKDEV(major, 0), NULL, "100ask_gpio_key"); /* /dev/100ask_gpio_key */
return 0;
}
static int gpio_key_remove(struct platform_device *pdev)
{
//int err;
struct device_node *node = pdev->dev.of_node;
int count;
int i;
device_destroy(gpio_key_class, MKDEV(major, 0));
class_destroy(gpio_key_class);
unregister_chrdev(major, "100ask_gpio_key");
count = of_gpio_count(node);
for (i = 0; i < count; i++)
{
free_irq(gpio_keys_100ask[i].irq, &gpio_keys_100ask[i]);
}
kfree(gpio_keys_100ask);
return 0;
}
static const struct of_device_id ask100_keys[] = {
{ .compatible = "100ask,gpio_key" },
{ },
};
/* 1. 定义platform_driver */
static struct platform_driver gpio_keys_driver = {
.probe = gpio_key_probe,
.remove = gpio_key_remove,
.driver = {
.name = "100ask_gpio_key",
.of_match_table = ask100_keys,
},
};
/* 2. 在入口函数注册platform_driver */
static int __init gpio_key_init(void)
{
int err;
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
err = platform_driver_register(&gpio_keys_driver);
return err;
}
/* 3. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数
* 卸载platform_driver
*/
static void __exit gpio_key_exit(void)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
platform_driver_unregister(&gpio_keys_driver);
}
/* 7. 其他完善:提供设备信息,自动创建设备节点 */
module_init(gpio_key_init);
module_exit(gpio_key_exit);
MODULE_LICENSE("GPL");
应用部分
在调用poll函数时,要指明:
① 你要监测哪一个文件:哪一个fd
② 你想监测这个文件的哪种事件:是POLLIN、还是POLLOUT
③ 在poll函数返回时,要判断状态。
完整代码
1 |
|
poll机制
从应用程序中的poll调用,最终会一步一步得调用到do_poll函数,具体流程;
poll -> drv_poll -> sys_poll -> do_sys_poll -> do_poll
do_poll
① 从这里开始,将会导致驱动程序的poll函数被第一次调用。
沿着②③④⑤,你可以看到:驱动程序里的poll_wait会调用__pollwait函数把线程放入某个队列。
当执行完①之后,在⑥或⑦处,pt->_qproc被设置为NULL,所以第二次调用驱动程序的poll时,不会再次把线程放入某个队列里。
⑧ 如果驱动程序的poll返回有效值,则count非0,跳出循环;
⑨ 否则休眠一段时间;当休眠时间到,或是被中断唤醒时,会再次循环、再次调用驱动程序的poll。
回顾APP的代码,APP可以指定“想等待某些事件”,poll函数返回后,可以知道“发生了哪些事件”:
驱动程序里怎么体现呢?在上上一个图中,看②位置处,细说如下: