poll機制可實現(xiàn)有數(shù)據(jù)的時候就去讀,沒有數(shù)據(jù)的時候,如果超過規(guī)定一個時間,就表示超時時間。poll機制需要應(yīng)用程序主動去讀,而異步通知并不需要,一旦設(shè)備就緒,則主動通知應(yīng)用程序,應(yīng)用程序不需要主動查詢設(shè)備狀態(tài),類似于中斷的概念,一個進程收到一個信號與處理器收到一個中斷請求可以說是一樣的。信號是異步的,一個進程不必通過任何操作來 等待信號的到達。
在linux中,異步通知是使用信號來實現(xiàn)的,而在linux,大概有30種信號,比如大家熟悉的ctrl+c的SIGINT信號,進程能夠忽略或者捕獲除過SIGSTOP和SIGKILL的全部信號,當信號背捕獲以后,有相應(yīng)的函數(shù)來處理它。
實現(xiàn)異步通知的四個要素:
一、應(yīng)用程序要實現(xiàn):注冊信號處理函數(shù),使用signal函數(shù);
二、誰來發(fā)?驅(qū)動來發(fā);
三、發(fā)給誰?發(fā)給應(yīng)用程序,但應(yīng)用程序必須告訴驅(qū)動PID;
四、怎么發(fā)?驅(qū)動程序使用kill_fasync函數(shù);
問:應(yīng)該在驅(qū)動的哪里調(diào)用kill_fasync函數(shù)?
答:kill_fasync函數(shù)的作用是,當有數(shù)據(jù)時去通知應(yīng)用程序,理所當然的應(yīng)該在用戶終端處理函數(shù)里調(diào)用。
問:file_operations需要添加什么函數(shù)指針成員嗎?
答:要的,需要添加fasync函數(shù)指針,要實現(xiàn)這個函數(shù)指針,幸運的是,這個函數(shù)僅僅調(diào)用了fasync_helper函數(shù),而且這個函數(shù)是內(nèi)核幫我們實現(xiàn)好了,驅(qū)動工程師不用修改,fasync_helper函數(shù)的作用是初始化/釋放fasync_struct
詳細請參考驅(qū)動源碼
#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;/* 用戶中斷處理函數(shù) */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;/* 表示中斷已經(jīng)發(fā)生 */ wake_up_interruptible(&button_waitq); /* 喚醒休眠的進程 */ /* 用kill_fasync函數(shù)告訴應(yīng)用程序,有數(shù)據(jù)可讀了 * button_fasync結(jié)構(gòu)體里包含了發(fā)給誰(PID指定) * SIGIO表示要發(fā)送的信號類型 * POLL_IN表示發(fā)送的原因(有數(shù)據(jù)可讀了) */ 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為相應(yīng)的外部中斷引腳 * IRQT_BOTHEDGE應(yīng)該改為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; * 當有按鍵按下時,發(fā)生中斷,在中斷處理函數(shù)會喚醒 * 即ev_press = 1; * 喚醒后,接著繼續(xù)將數(shù)據(jù)通過copy_to_user函數(shù)傳遞給應(yīng)用程序 */ 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; /* 該函數(shù),只是將進程掛在button_waitq隊列上,而不是立即休眠 */ poll_wait(file, &button_waitq, wait); /* 當沒有按鍵按下時,即不會進入按鍵中斷處理函數(shù),此時ev_press = 0 * 當按鍵按下時,就會進入按鍵中斷處理函數(shù),此時ev_press被設(shè)置為1 */ if(ev_press) { mask |= POLLIN | POLLRDNORM; /* 表示有數(shù)據(jù)可讀 */ } /* 如果有按鍵按下時,mask |= POLLIN | POLLRDNORM,否則mask = 0 */ return mask;}/* 當應(yīng)用程序調(diào)用了fcntl(fd, F_SETFL, Oflags | FASYNC); * 則最終會調(diào)用驅(qū)動的fasync函數(shù),在這里則是fifth_drv_fasync * fifth_drv_fasync最終又會調(diào)用到驅(qū)動的fasync_helper函數(shù) * fasync_helper函數(shù)的作用是初始化/釋放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,};/* 驅(qū)動入口函數(shù) */static int fifth_drv_init(void){ /* 主設(shè)備號設(shè)置為0表示由系統(tǒng)自動分配主設(shè)備號 */ major = register_chrdev(0, "fifth_drv", &fifth_drv_fops); /* 創(chuàng)建fifthdrv類 */ fifthdrv_class = class_create(THIS_MODULE, "fifthdrv"); /* 在fifthdrv類下創(chuàng)建buttons設(shè)備,供應(yīng)用程序打開設(shè)備*/ fifthdrv_device = device_create(fifthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); return 0;}/* 驅(qū)動出口函數(shù) */static void fifth_drv_exit(void){ unregister_chrdev(major, "fifth_drv"); device_unregister(fifthdrv_device); //卸載類下的設(shè)備 class_destroy(fifthdrv_class); //卸載類}module_init(fifth_drv_init); //用于修飾入口函數(shù)module_exit(fifth_drv_exit); //用于修飾出口函數(shù)MODULE_AUTHOR("LWJ");MODULE_DESCRIPTION("Just for Demon");MODULE_LICENSE("GPL"); //遵循GPL協(xié)議
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
應(yīng)用測試程序源碼
#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 * 告訴內(nèi)核,發(fā)給誰 */ fcntl(fd, F_SETOWN, getpid()); /* F_GETFL :Read the file status flags * 讀出當前文件的狀態(tài) */ 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); * 修改當前文件的狀態(tài),添加異步通知功能 */ fcntl(fd, F_SETFL, flag | FASYNC); while(1) { /* 為了測試,主函數(shù)里,什么也不做 */ 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
當無按鍵按下時,應(yīng)用測試程序一直在sleep,當有按鍵按下時,signal會被調(diào)用,最終會調(diào)用mysignal_fun,在此函數(shù)里read(fd, &key_val, 1);會去讀出按鍵值,這樣一來,應(yīng)用程序就相當于不用主動去讀數(shù)據(jù)了,每當驅(qū)動里有數(shù)據(jù)時,就會告訴應(yīng)用程序有數(shù)據(jù)了,此時read函數(shù)才會被調(diào)用。
總結(jié)
為了使設(shè)備支持異步通知機制,驅(qū)動程序中涉及以下3項工作:
支持F_SETOWN命令,能在這個控制命令處理中設(shè)置filp->f_owner為對應(yīng)進程ID。? 不過此項工作已由內(nèi)核完成,設(shè)備驅(qū)動無須處理。
支持F_SETFL命令的處理,每當FASYNC標志改變時,驅(qū)動程序中的fasync()函數(shù)將得以執(zhí)行。驅(qū)動中應(yīng)該實現(xiàn)fasync()函數(shù)。
在設(shè)備資源可獲得時,調(diào)用kill_fasync()函數(shù)激發(fā)相應(yīng)的信號
應(yīng)用程序:
fcntl(fd, F_SETOWN, getpid()); // 告訴內(nèi)核,發(fā)給誰Oflags = fcntl(fd, F_GETFL);fcntl(fd, F_SETFL, Oflags | FASYNC); // 改變fasync標記,最終會調(diào)用到驅(qū)動的faync > fasync_helper:初始化/釋放fasync_s
?
評論
查看更多