From: http://blog.csdn.net/wenhui_/article/details/6824411
异步通知的意思是:一旦设备就绪,则主动通知应用程序,这样应用程序就根本不需要查询设备的状态,
这一点非常类似于硬件上的“中断”的概念,比较准确的称谓是“信号驱动的异步I/O”。信号是在软件层次上对
中断机制的一种模拟,在原理上一个进程接收到一个信号与处理器接收到一个中断请求是一样的。
1>在把驱动从2.6.32 移植到2.6.36时 报错
/home/kernel_test/globalfifo/globalfifo.c:240:2: error: unknown field 'ioctl' specified in initializer才发现2.6.36的file_operations结构发生了重大变化。(但基本的思想还是不变的)
取消了原有的ioctl成员,添加来新的成员 (变化部分在下面源码中用绿色字体)- long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
- long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
2>/home/kernel_test/globalfifo/globalfifo.c:245:2: warning: initialization from incompatible pointer type
出现此种warnning 的原因 “不兼容的指针类型初始化”
是你定义的函数类型与接口函数的类型不一样如 把 把返回值 long 定义成了 int
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
少写const 也会引起 warning 因为在这里const所起的作用是 避免函数使用指针去改变了传进来的值
以下是增加(红色字体)异步通知后的globalfifo 的驱动程序:
1 /*2 * a globalfifo driver as example of char device drivers
3 * This example is to introduce poll, blocking and non-blocking access
4 *
5 *The initial developer of the original code is Baohua Song
6 *<auther@linuxdriver.cn>. All Rights Reserved
7 *
8 * 1>只当FIFO 中有数据时(有进程把数据写到这个 FIFO而且没有被 读进程 读空)
9 * 读进程才能把数据读出,读出后数据 从 FIFO 中拿掉
10 * 2>只有当FIFO 非满时(即还有空间未被读写或满后被读进程读出了数据)
11 * 写进程才能往里面写数据,
12 * 这样 读唤醒写 写唤醒读
13 */
14
15 #include<linux/module.h>
16 #include<linux/types.h>
17 #include<linux/fs.h> /*异步通知机制 fasync*/
18 #include<linux/errno.h>
19 #include<linux/mm.h>
20 #include<linux/sched.h>
21 #include<linux/init.h>
22 #include<linux/cdev.h>
23 #include<asm/io.h>
24 #include<asm/system.h>
25 #include<asm/uaccess.h>
26 #include<linux/poll.h>
27 #include <linux/ioctl.h>
28
29 /*不加此头文件在linux-2.6.36会报错但在linux-2.6.32.2不会*/
30 /*使用的是arm-linux-gcc-4.5.1编译器*/
31 #include<linux/slab.h>
32
33 #define DEBUG
34
35 #define GLOBALFIFO_SIZE 10 /*全局fifo最大10字节 方便测试写满*/
36 #define FIFO_CLEAR 0X1 /*清0全局内存的长度*/
37 #define GLOBALFIFO_MAJOR 249 /*预设的globalfifo 的主设备号*/
38
39 static int globalfifo_major = GLOBALFIFO_MAJOR;
40
41 /*globalfifo设备结构体*/
42 struct globalfifo_dev{
43 struct cdev cdev; /*cdev结构体*/
44 unsigned int current_len; /*fifo有效数据长度*/
45 unsigned char mem[GLOBALFIFO_SIZE]; /*全局内存*/
46 struct semaphore sem; /*并发控制用的信号量*/
47 wait_queue_head_t r_wait; /*阻塞读用的等待队列 内核 双向 循环 链表 都可以 为头*/
48 wait_queue_head_t w_wait; /*阻塞写用的等待队列头*/
49 struct fasync_struct *async_queue; /*异步结构体指针,用于读*/
50 };
51
52 struct globalfifo_dev *globalfifo_devp; /*设备结构体指针*/
53
54 /*增加支持异步通知的函数 在release 函数中调用 */
55 static int globalfifo_fasync(int fd, struct file *filp, int mode)
56 {
57 struct globalfifo_dev *dev = filp->private_data;
58
59 /*将文件从异步通知列表(相关进程列表)中删除*/
60 return fasync_helper(fd, filp, mode, &dev->async_queue);61 }
62
63 /*globalfifo读函数*/
64 static ssize_t globalfifo_read(struct file *filp, char __user *buf, size_t c ount, loff_t *ppos)
65 {
66 int ret;
67 struct globalfifo_dev *dev = filp->private_data;
68 DECLARE_WAITQUEUE(wait, current);
69
70 down(&dev->sem); /*获得信号量*/
71 add_wait_queue(&dev->r_wait, &wait); /*加入读等待队列头 到内核*/
72
73 /*等待FIFO 非空*/
74 if(dev->current_len == 0){
75 if(filp->f_flags & O_NONBLOCK){ /*如果进程为 非阻塞打开 设备文件*/
76 ret = -EAGAIN;
77 goto out;
78 }
79 __set_current_state(TASK_INTERRUPTIBLE); /*改变进程状态为睡眠*/
80 up(&dev->sem); /*释放信号量*/
81
82 schedule(); /*调度其他进程执行*/
83 if(signal_pending(current)){
84 /*如果是因为信号唤醒*/
85 ret = -ERESTARTSYS;
86 goto out2;
87 }
88 down(&dev->sem);
89 }
90
91 /*拷贝到用户空间*/
92 if(count > dev->current_len)
93 count = dev->current_len;
94 if(copy_to_user(buf, dev->mem, count)){
95 ret = -EFAULT;
96 goto out;
97 }else{
98 memcpy(dev->mem, dev->mem + count, dev->current_len - count);/*fifo> 数据前移*/
99 dev->current_len -= count; /*有效数据长度减少*/
100 printk(KERN_INFO"read %d bytes(s),current_len:%d\n",count, dev->curr ent_len);
101
102 wake_up_interruptible(&dev->w_wait); /*唤醒写等待队列*/
103 ret = count;
104 }
105 out:
106 up(&dev->sem); /*释放信号量*/
107 out2:
108 remove_wait_queue(&dev->w_wait, &wait); /*从属的等待队列头移除*/
109 set_current_state(TASK_RUNNING);
110 return ret;
111 }
112
113 /*globalfifo 写操作*/
114 static ssize_t globalfifo_write(struct file *filp, const char __user *buf, s ize_t count, loff_t *ppos)
115 {
116 struct globalfifo_dev *dev = filp->private_data;
117 int ret;
118 DECLARE_WAITQUEUE(wait, current); /*定义等待队列*/
119
120 down(&dev->sem); /*获得信号量*/
121 add_wait_queue(&dev->w_wait, &wait); /*进入写等待队列头*/
122
123 /*等待FIFO非满*/
124 if(dev->current_len == GLOBALFIFO_SIZE){
125 if(filp->f_flags & O_NONBLOCK){ /*如果进程非阻塞打开的文件*/
126 ret = -EAGAIN;
127 goto out;
128 }
129
130 __set_current_state(TASK_INTERRUPTIBLE); /*改变进程状态为睡眠*/
131 up(&dev->sem); /*释放信号量*/
132
133 schedule(); /*调度其他进程执行*/
134 if(signal_pending(current)){
135 /*如果是因为信号唤醒*/
136 ret = -ERESTARTSYS;
137 goto out2;
138 }
139 down(&dev->sem); /*获得信号量*/
140 }
141
142 /*从用户空间拷贝数据到内核空间*/
143 if(count > GLOBALFIFO_SIZE - dev->current_len){
144 /*如果要拷贝的数据大于 剩余有效内存长度
145 *则 只拷贝最大 能装下的长度
146 */
147 count = GLOBALFIFO_SIZE - dev->current_len;
148 }
149 if(copy_from_user(dev->mem + dev->current_len, buf, count)){
150 ret = -EFAULT;
151 goto out;
152 }else {
153 dev->current_len += count;
154 printk(KERN_INFO"written %d bytes(s), current_len: %d\n",count, dev- >current_len);
155
156 wake_up_interruptible(&dev->r_wait); /*唤醒读等待队列*/
157
158 /*产生异步读信号通知相关进程 把要发送的SIGIO 信号发送出去*/
159 if(dev->async_queue){
160 kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
161 #ifdef DEBUG
162 printk("<0>%s kill SIGIO\n", __func__);
163 #endif
164 }
165 ret = count;
166 }
167 out:
168 up(&dev->sem); /*释放信号量*/
169 out2:
170 remove_wait_queue(&dev->w_wait, &wait); /*从附属的等待队列头移除*/
171 set_current_state(TASK_RUNNING);
172 return ret;
173 }
174
175
176 /*ioctl 设备控制函数*/
177 //static int globalfifo_ioctl(struct inode *inodep, struct file *filp, unsig ned int cmd, unsigned long arg)
178 static long globalfifo_ioctl(struct file *filp, unsigned int cmd, unsigned l ong arg)
179 {
180 struct globalfifo_dev *dev = filp->private_data;/*获得设备结构体指针*/
181
182 switch(cmd){
183 case FIFO_CLEAR:
184 down(&dev->sem); /*获得信号量*/
185 dev->current_len = 0;
186 memset(dev->mem, 0, GLOBALFIFO_SIZE);
187 up(&dev->sem); /*释放信号量*/
188
189 printk(KERN_INFO"globalfifo is set to zero\n");
190 break;
191
192 default:
193 return -EINVAL;
194 }
195 return 0;
196 }
197
198 /*在驱动中的增加轮询操作*/
199 static unsigned int globalfifo_poll(struct file *filp, poll_table *wait)
200 {
201 unsigned int mask = 0;
202 struct globalfifo_dev *dev = filp->private_data;/*获得设备结构体指针*/
203
204 down(&dev->sem);
205 poll_wait(filp, &dev->r_wait, wait);
206 poll_wait(filp, &dev->w_wait, wait);
207
208 /*fifo非空*/
209 if(dev->current_len != 0){
210 mask |= POLLIN | POLLRDNORM; /*标示数据可以获得*/
211 }
212
213 /*fifo 非满*/
214 if(dev->current_len != GLOBALFIFO_SIZE){
215 mask |= POLLOUT | POLLWRNORM ; /*标示数据可以写入*/
216 }
217
218 up(&dev->sem);
219 return mask; /*返回驱动是否可读 或可写的 状态*/
220 }
221
222 /*文件打开函数*/
223 int globalfifo_open(struct inode *inode, struct file *filp)
224 {
225 /**/
226 filp->private_data = globalfifo_devp;
227 return 0;
228 }
229
230 /*文件释放函数*/
231 int globalfifo_release(struct inode *inode, struct file *filp)
232 {
233 /*将文件从异步通知列表中删除*/
234 globalfifo_fasync(-1, filp, 0);
235 return 0;
236 }
237
238 /*文件操作结构体*/
239 static const struct file_operations globalfifo_fops = {
240 .owner = THIS_MODULE,
241 .read = globalfifo_read,
242 .write = globalfifo_write,
243 //.ioctl = globalfifo_ioctl,
244 .unlocked_ioctl = globalfifo_ioctl,
245 .poll = globalfifo_poll,
246 .fasync = globalfifo_fasync, /*不要忘了在这里也要加上 与内核联系的接口*/
247 .open = globalfifo_open,
248 .release = globalfifo_release,
249 };
250
251 /*初始化并注册cdev*/
252 static void globalfifo_setup_cdev(struct globalfifo_dev *dev, int index)
253 {
254 int err, devno = MKDEV(globalfifo_major, index);
255
256 cdev_init(&dev->cdev, &globalfifo_fops);
257 dev->cdev.owner = THIS_MODULE;
258 err = cdev_add(&dev->cdev, devno, 1);
259 if(err)
260 printk(KERN_NOTICE "Error %d adding LED %d", err, index);
261 }
262
263 /*设备驱动模块加载函数*/
264 int globalfifo_init(void)
265 {
266 int ret;
267 dev_t devno = MKDEV(globalfifo_major, 0);
268
269 /*申请设备号*/
270 if(globalfifo_major)
271 ret = register_chrdev_region(devno, 1, "globalfifo");
272 else{/*动态申请设备号*/
273 ret = alloc_chrdev_region(&devno, 0, 1, "globalfifo");
274 globalfifo_major = MAJOR(devno);
275 }
276
277 if(ret < 0)
278 return ret;
279
280 /*动态申请设备结构体的内存*/
281 globalfifo_devp = kmalloc(sizeof(struct globalfifo_dev), GFP_KERNEL);
282 if(!globalfifo_devp){
283 ret = - ENOMEM;
284 goto fail_malloc;
285 }
286
287 memset(globalfifo_devp, 0, sizeof(struct globalfifo_dev));
288
289 globalfifo_setup_cdev(globalfifo_devp, 0);
290
291 init_MUTEX(&globalfifo_devp->sem); /*初始化信号量*/
292 init_waitqueue_head(&globalfifo_devp->r_wait); /*初始化读等待队列头*/
293 init_waitqueue_head(&globalfifo_devp->w_wait); /*初始化写等待队列头*/
294
295 return 0;
296
297 fail_malloc: unregister_chrdev_region(devno, 1);
298 return ret;
299 }
300
301 void globalfifo_exit(void)
302 {
303 cdev_del(&globalfifo_devp->cdev); /*注销cdev*/
304 kfree(globalfifo_devp); /*释放设备结构体内存*/
305 unregister_chrdev_region(MKDEV(globalfifo_major, 0), 1); /*释放设备号*/
306 }
307
308 MODULE_AUTHOR("Song Baohua");
309 MODULE_LICENSE("Dual BSD/GPL");
310
311 //module_param()
312
313 module_init(globalfifo_init);
314 module_exit(globalfifo_exit);
测试应用程序:
- //app_glo_signal.c
- 1 #include<stdio.h>
- 2 #include<stdlib.h>
- 3 #include<unistd.h>
- 4 #include<fcntl.h>
- 5 #include<signal.h>
- 6 #include<sys/stat.h>
- 7
- 8 //#define DEBUG
- 9
- 10 #ifdef DEBUG
- 11 #define MAX_LEN 100
- 12 #endif
- 13
- 14 /*接受到异步读信号后的动作*/
- 15 void input_handler(int signum)
- 16 {
- 17 #ifdef DEBUG
- 18 char data[MAX_LEN];
- 19 int len;
- 20 /*读取并输出SIDIN_FILENO 上的输入*/
- 21 len = read(fd, &data, MAX_LEN);
- 22 data[len] = '\0';
- 23 printf("input available:%s", data);
- 24
- 25 #endif
- 26 printf("receive a signal from globalfifo, signalnum:%d\n",signum);
- 27 }
- 28
- 29 int main(void)
- 30 {
- 31 int fd,oflags;
- 32 fd = open("/dev/globalfifo", O_RDWR, S_IRUSR | S_IWUSR);
- 33 if(fd != -1){
- 34 /*启动信号驱动机制*/ /*设置信号处理函数*/
- 35 signal(SIGIO, input_handler);/*让input_handler()函数处理SIGIO信号*/
- 36 fcntl(fd, F_SETOWN, getpid()); /*通过F_SETOWN IO控制命令设置设备文件拥有者为本进程*/
- 37 oflags = fcntl(fd, F_GETFL);/*F_GETFL 命令 取得fd设备文件状态标志*/
- 38 fcntl(fd, F_SETFL, oflags | FASYNC); /*F_SETFL 命令设置设备文件支持FASYNC(异步通知模式)*/
- 39 while(1){
- 40 sleep(100);
- 41 }
- 42 }else{
- 43 printf("device open failure\n");
- 44 }
- 45 }
参考:《LINUX设备驱动开发详解》 (第 2 版) 宋宝华