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 < 0) { printf("open error "); } /* F_SETOWN: Set the process ID * 告訴內核,發給誰 */ fcntl(fd, F_SETOWN, getpid()); /* F_GETFL :Read the file status flags * 讀出當前文件的狀態 */ flag = fcntl(fd, F_GETFL); /* F_SETFL: Set the file status flags to the value specified by arg * int fcntl(int fd, int cmd, long arg); * 修改當前文件的狀態,添加異步通知功能 */ fcntl(fd, F_SETFL, flag | FASYNC); while(1) { /* 為了測試,主函數里,什么也不做 */ sleep(1000); } return 0;}
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
當無按鍵按下時,應用測試程序一直在sleep,當有按鍵按下時,signal會被調用,最終會調用mysignal_fun,在此函數里read(fd, &key_val, 1);會去讀出按鍵值,這樣一來,應用程序就相當于不用主動去讀數據了,每當驅動里有數據時,就會告訴應用程序有數據了,此時read函數才會被調用。
總結
為了使設備支持異步通知機制,驅動程序中涉及以下3項工作:
支持F_SETOWN命令,能在這個控制命令處理中設置filp->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
?
評論
查看更多