把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. RHEL 6.1字符界面无法登录SSH却能登录

    1.具体版本: 2.具体现象: 每次输入用户名密码登录之后又跳到这个界面.但是用ssh却可以登录. 3.查看日志 [root@localhost ~]# tail -f /var/log/secure ...

  2. about家庭智能设备部分硬件模块功能共享【协同工作】solution

    本人设备列表: Onda tablet {Android} wifi Desktop computer {win7.centos7} 外接蓝牙adapter PS interface 键盘.鼠标{与同 ...

  3. 创建第一个UI

    创建一个2D UI 制作UI时,首先要创建UI的"根".在Unity顶部NGUI菜单中选择Create,然后选择2D UI. 创建完成后,在Scene窗口中,NGUI自动生成了一个 ...

  4. online web design tool

    https://webflow.com/ http://css-tricks.com/snippets/ https://www.fluidui.com/demos

  5. JAVA入门第二季(mooc-笔记)

    相关信息 /** * @subject <学习与创业>作业1 * @author 信管1142班 201411671210 赖俊杰 * @className <JAVA入门第二季&g ...

  6. [BZOJ 2212] [Poi2011] Tree Rotations 【线段树合并】

    题目链接:BZOJ - 2212 题目分析 子树 x 内的逆序对个数为 :x 左子树内的逆序对个数 + x 右子树内的逆序对个数 + 跨越 x 左子树与右子树的逆序对. 左右子树内部的逆序对与是否交换 ...

  7. How to Cracked Sublime Text 3 Build 3065 in Ubuntu (Linux)

    整理自How to Cracked Sublime Text 3 Build 3065 in Ubuntu (Linux) Sublime Text 3 Build 3065 Release Date ...

  8. Swift 中的 Runtime

    即使在 Swift APP 中没有一行 Object-c 的代码,每个 APP 也都会在 Object-c runtime 中运行,为动态任务分发和运行时对象关联开启了一个世界.更确切地说,可能在仅使 ...

  9. IntelliJ IDEA 中文乱码解决

    显示乱码的主要原因,编辑器自带的字体里没有对中文的支持, 将字体换成有中文支持的即可,例如:为微软雅黑.宋体.... 字体转换位置 Ctrl+Alt+S     > Appearance > ...

  10. 关于Failed to convert property value of type [org.quartz.impl.StdScheduler] to required type [org.springframework.scheduling.quartz.SchedulerFactoryBean

    在一个业务类有下列属性 private SchedulerFactoryBeanscheduler; public SchedulerFactory BeangetScheduler() { retu ...