把globalmem中的全局内存变成一个FIFO,只有当FIFO中有数据的时候(即有进程把数据写到这个FIFO而且没有被读进程读空),读进程才能把数据读出,而且读取后的数据会从globalmem的全局内存中被拿掉;只有当fifo非满时(即还有一些空间未被写,或写满后被读进程读出了数据),写进程才能写入数据。现在将globalmem重命名为“globalfifo",在globalfifo中,读fifo将唤醒写fifo,写fifo也将唤醒读fifo.

(一) 支持阻塞操作的globalfifo设备驱动

在globalfifo设备结构体上需要添加两个等待队列头,分别对应于读和写。

struct globalfifo_dev

{

struct cdev cdev;                            //cdev结构体

unsigned int current_len;                    //fifo有效数据长度

unsigned char mem[GLOBALFIFO_SIZE];          //全局内存

struct semaphore sem;                        //并发控制用的信号量

wait_queue_head_t r_wait;                    //阻塞读用的等待队列头

wait_queue_head_t w_wait;                    //阻塞写用的等待dui lie

};

等待队列需要在设备驱动模块加载函数中调用init_waitqueue_head()被初始化。

int gloaalfifo_init(void)

{

int ret;

dev_t devno=MKDEV(globalfifo_major,0);

//申请设备号

if(globalfifo_major)

ret=register_chrdev_region(devno,1,"globalfifo");

else

{          //动态申请设备号

ret=alloc_chrdev_region(&devno,0,1,"globalfifo");

globalfifo_major=MAJOR(devno);

}

if(ret<0)

return ret;

//动态申请设备结构体的内存

globalfifo_devp=kmalloc(sizeof(struct globalfifo_dev),GFP_KERNEL);

if(!globalfifo_devp)

{

ret=-ENOMEM;

goto fail_malloc;

}

memset(globalfifo_devp,0,sizeof(struct_globalfifo_dev));

globalfifo_setup_cdev(globalfifo_devp,0);

init_MUTEX(&globalfifo_devp->sem);                 //初始化信号量

init_waitqueue_head(&globalfifo_devp->r_wait);      //初始化读等待队列

init_waitqueue_head(&globalfifo_devp->w_wait);      //初始化写等待队列

return 0;

fail_malloc:unregister_chrdev_region(devno,1);

return ret;

}

设备驱动读函数中应该增加等待globalfifo_devp->w_wait被唤醒的语句;而在写操作中唤醒globalfifo_devp->r_wait.

static ssize_t globalfifo_read(struct file *filp,char_user *buf,size_t count,loff_t *ppos)

{

int ret;

struct globalfifo_dev *dev=filp->private_data;       //获得设备结构体指针

DECLARE_WAITQUEUE(wait,current);                     //定义等待队列

down(&dev->sem);                                     //获得信号量

add_wait_queue(&dev->r_wait,&wait);                  //进入读等待队列头

//等待fifo非空

while(dev->current_len==0)

{

if(filp->f_flags &o_nonblock)

{

ret=-EAGAIN;

goto out;

}

_set_current_state(TASK_INTERRUPTIBLE);           //改变进程状态为睡眠

up(&dev->sem);

schedule();                                        //调度其他进程执行

if(signal_pending(current))                        //如果是因为信号唤醒

{

ret=-ERESTARTSYS;

goto out2;

}

down(&dev->sem);

}

//拷贝到用户空间

if(count > dev->current_len)

count=dev->current_len;

if(copy_to_user(buf,dev->mem,count)

{

ret=-EFAULT;

goto out;

}

else

{

memcpy(dev->mem,dev->mem+count,dev->current_len-count);         //fifo数据前移

dev->current_len-=count;                                        //有效数据长度减少。

printk(KERN_INFO "read %d bytes(s),current_len:%d\n",count,dev->current_len);

wake_up_interruptible(&dev->w_wait);                             //唤醒写等待队列

ret=count;

}

out:up(&dev->sem);                                                 //释放信号量

out2:remove_wait_queue(&dev->w_wait,&wait);                        //移除等待队列

set_current_state(TASK_RUNNING);

return ret;

}

//globalfifo写操作

static ssize_t globalfifo_write(struct file *filp,const char _ _user *buf,size_t count, loff_t *ppos)

{

struct globalfifo_dev *dev=filp->private_data;          //获得设备结构体指针

int ret;

DECLARE_WAITQUEUE(&dev->w_wait,&wait);                  //进入写等待队列

down(&dev->sem);                                        //获得信号量

add_wait_queue(&dev->w_wait,&wait);                     //进入写等待队列头

//等待fifo非满

while(dev->current_len=GLOBALFIFO_SIZE)

{

if(filp->f_flags &O_NONBLOCK )

{

//如果是非阻塞访问

ret=-EAGAIN;

goto out;

}

_set_current_state(TASK_INTERRUPTIBLE);  //改变进程状态为睡眠

up(&dev->sem);

schedule();                    //调度其它进程执行

if(signal_pending(current))

{

//如果是因为信号唤醒

ret=-ERESTARTSYS;

goto out2;

}

down(&dev->sem);

}

if(count>GLOBALFIFO_SIZE-dev->current_len)

count=GLOBALFIFO_SIZE-dev->current_len;

if(copy_from_user(dev->mem+dev->current_len,buf,count))

{

ret=-EFAULT;

goto out;

}

else

{
     dev->current_len+=count;

printk(KERNEL_INFO"written %d byte(S),current_len:%d\n",count,dev->current_len);

wake_up_interruptible(&dev->r_wait);     //唤醒读等待队列

ret=count;

}

out:up(&dev->sem);

out2:remove_wait_queue(&dev->w_wait,&wait);

set_current_state(TASK_RUNNING);

return ret;

}

(二)支持轮询操作的globalfifo驱动

在global的poll()函数中,首先将设备结构体中的r_wait和w_wait等待队列添加到等待列表,然后通过判断dev->current_len是否等于0来获得设备的可读状态,通过判断dev->current_len是否等于GLOBALFIFO_SIZE来获得设备的可写状态。

  global设备驱动的poll()函数

static unsigned int globalfifo_poll(struct file *filp,poll_table *wait)

{

unsigned int mask=0;

struct globalfifo_dev *dev=filp->private_data;     //获得设备结构体指针

down(&dev-sem);                                    //获得信号量

poll_wait(filp,&dev->r_wait,wait);

poll_wait(filp,&dev->w_wait,wait);

//fifo非空

if(dev->current_len!=0)

mask|=POLLIN|POLLRDNORM;             //标示数据可获得

if(dev->current_len!=GLOBALFIFO_SIZE)

mask|=POLLOUT|POLLWRNORM;            //标示数据可写入

up(&dev->sem);          //释放信号量

return mask;

}

注意要把globalfifo_poll赋给globalfifo_fops的poll成员。

                                      (三) 支持异步通知的globalfifo设备驱动

首先应该将异步通知的结构体指针加入到globalfifo设备的结构体里面去。

struct globalfifo_dev

{

struct cdev cdev;                            //cdev结构体

unsigned int current_len;                    //fifo有效数据长度

unsigned char mem[GLOBALFIFO_SIZE];          //全局内存

struct semaphore sem;                        //并发控制用的信号量

wait_queue_head_t r_wait;                    //阻塞读用的等待队列头

wait_queue_head_t w_wait;                    //阻塞写用的等待dui lie

struct fasync_struct *async_queue;           //异步结构体指针,用于读

};

  支持异步通知的globalfifo设备驱动fasync()函数

static int globalfifo_fasync(int fd,struct file *filp,int mode)

{

struct globalfifo_dev *dev=filp->private_data;

return fasync_helper(fd,filp,mode,&dev->async_queue);

}

支持异步通知的globalfifo设备驱动写函数

static ssize_t globalfifo_write(struct file *filp,const char _user *buf,size_t count,loff_t *ppos)

{

struct globalfifo_dev *dev=filp->private_data;                 //获得设备结构体指针

int ret;

DECLARE_WAITQUEUE(wait,current);                               //定义等待队列

down(&dev->sem);                                               //取得信号量

add_wait_queue(&dev->w_wait,&wait);                            //进入写等待队列

//等待fifo未满

if(dev->current_len==GLOBALFIFO_SIZE)

{

if(filp->f_flags&O_NOBLOCK)

{           //如果是非阻塞访问

ret=-EAGAIN;

goto out;

}

_set_current_state(TASK_INTERRUPTIBLE)                 //改变进程状态为睡眠

up(&dev->sem);

schedule();

if(signal_pending(current)

ret=-ERESTARTSYS;

goto out2;

}

down(&dev->sem);

}

//从用户空间拷贝到内核空间

if(count >GLOBALFIFO_SIZE- dev->current_len)

count=GLOBALFIFO_SIZE-dev->current_len;

if(copy_form_user(dev->mem+dev->current_len,buf,count))

{
     ret=-EFAULT;

goto out;

}

else

{

dev->current_len+=count;

wake_up_interruptible(&dev->r-wait);      //唤醒读等待队列

}

//产生异步读信号

if(dev->async_queue)

kill_fasync(&dev->async_queue,SIGIO,POLL_IN);

ret=count;

}

out:up(&dev->sem);

out2:remove_wait_queue(&dev->w_wait,&wait);

set_current_state(TASK_RUNNING);

return ret;

}

最后记得在release()函数里用globalfifo_fasync()函数将文件从异步通知列表中删除

int globalfifo_release(struct inode *inode,struct file *filp)

{

globalfifo_fasync(-1,filp,0);

return 0;

}

globalfifo设备驱动的更多相关文章

  1. 支持阻塞操作和轮询操作的globalfifo设备驱动代码分析以及测试代码

    #include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include ...

  2. 蜕变成蝶~Linux设备驱动中的阻塞和非阻塞I/O

    今天意外收到一个消息,真是惊呆我了,博客轩给我发了信息,说是俺的博客文章有特色可以出本书,,这简直让我受宠若惊,俺只是个大三的技术宅,写的博客也是自己所学的一些见解和在网上看到我一些博文以及帖子里综合 ...

  3. platform设备驱动全透析

    原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://21cnbao.blog.51cto.com/109393/337609 1.1 ...

  4. 《Linux设备驱动开发具体解释(第3版)》进展同步更新

    本博实时更新<Linux设备驱动开发具体解释(第3版)>的最新进展. 2015.2.26 差点儿完毕初稿. 本书已经rebase到开发中的Linux 4.0内核,案例多数基于多核CORTE ...

  5. 《linux设备驱动开发详解》笔记——12linux设备驱动的软件架构思想

    本章重点讲解思想.思想.思想. 12.1 linux驱动的软件架构 下述三种思想,在linux的spi.iic.usb等复杂驱动里广泛使用.后面几节分别对这些思想进行详细说明. 思想1:驱动与设备分离 ...

  6. 七、设备驱动中的阻塞与非阻塞 IO(二)

    7.2 轮询 7.2.1 介绍 在用户程序中的 select() 和 poll() 函数最终会使设备驱动中的 poll() 函数被执行. 设备驱动程序中的轮询函数原型: /** 用于询问设备是否可以非 ...

  7. 七、设备驱动中的阻塞与非阻塞 IO(一)

    7.1 阻塞与非阻塞 IO 阻塞操作是指在执行设备操作的时候,若不能获取资源,则挂起进程直到满足可操作的条件后再进行操作.被挂起的进程进入睡眠状态,被从调度器的运行队列移走,直到等待的条件被满足. 非 ...

  8. Linux字符设备驱动框架

    字符设备是Linux三大设备之一(另外两种是块设备,网络设备),字符设备就是字节流形式通讯的I/O设备,绝大部分设备都是字符设备,常见的字符设备包括鼠标.键盘.显示器.串口等等,当我们执行ls -l ...

  9. 《连载 | 物联网框架ServerSuperIO教程》- 3.设备驱动介绍

    1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架Serve ...

随机推荐

  1. 干货:Web应用上线之前程序员应该了解的技术细节

    [伯乐在线注]:<Web 应用上线前,程序员应考虑哪些技术细节呢?>这是 StackExchange 上面的一个经典问题贴. 最赞回复有 2200+ 顶,虽然大多数人可能都听过其中大部分内 ...

  2. SWFUpload 按钮显示问题

    问题: 今天遇到一个这样的问题,我用Vs2010打开用SWFUpload上传文件的网页时,按钮显示不出来,试了好多方法,终于被我找到了! 解决方法: 原来是vs2010自带的Asp.net Devel ...

  3. 如何学习C++[转]

    关于学C++, 我向你推荐一些书(当然能够结合课内项目实践更好) 1.The C++ Programming Language(Bjarne Stroustrup)2. Inside The C++ ...

  4. html5判断用户摇晃了手机(转)

    先来看下html5的这几个特性: 1.deviceOrientation:方向传感器数据的事件,通过监听该事件可以获取手机静态状态下的方向数据: 2.deviceMotion: 运动传感器数据事件,通 ...

  5. 实战中总结出来的CSS常见问题及解决办法

    一.ul标签在Mozilla中默认是有padding值的,而在IE中只有margin有值. 二.同一个的class选择符可以在一个文档中重复出现,而id选择符却只能出现一次.对 一个标签同时使用cla ...

  6. WebKit Web Inspector增加覆盖率分析和类型推断功能

    WebKit中的Web Inspector(Web检查器)主要用于查看页面源代码.实时DOM层次结构.脚本调试.数据收集等,日前增加了两个十分有用的新功能:覆盖率分析和类型推断.覆盖率分析工具能够可视 ...

  7. about mobile web

    http://blog.csdn.net/kavensu/article/details/8722268 http://cavenfeng.iteye.com/blog/1551516 http:// ...

  8. Java异常处理之throws抛出异常

    package com.test; import java.io.FileReader; public class Test2 { public static void main(String[] a ...

  9. SLF4J user manual

    http://www.slf4j.org/manual.html The Simple Logging Facade for Java (SLF4J) serves as a simple facad ...

  10. 盘点国内程序员不常用的热门iOS第三方库

    https://github.com/syedhali/EZAudio 基于核心音频,有助于进行实时,低延迟音频处理和可视化的iOS和OSX音频可视化框架. https://github.com/ba ...