globalfifo设备驱动
把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 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设备驱动的更多相关文章
- 支持阻塞操作和轮询操作的globalfifo设备驱动代码分析以及测试代码
#include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include ...
- 蜕变成蝶~Linux设备驱动中的阻塞和非阻塞I/O
今天意外收到一个消息,真是惊呆我了,博客轩给我发了信息,说是俺的博客文章有特色可以出本书,,这简直让我受宠若惊,俺只是个大三的技术宅,写的博客也是自己所学的一些见解和在网上看到我一些博文以及帖子里综合 ...
- platform设备驱动全透析
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://21cnbao.blog.51cto.com/109393/337609 1.1 ...
- 《Linux设备驱动开发具体解释(第3版)》进展同步更新
本博实时更新<Linux设备驱动开发具体解释(第3版)>的最新进展. 2015.2.26 差点儿完毕初稿. 本书已经rebase到开发中的Linux 4.0内核,案例多数基于多核CORTE ...
- 《linux设备驱动开发详解》笔记——12linux设备驱动的软件架构思想
本章重点讲解思想.思想.思想. 12.1 linux驱动的软件架构 下述三种思想,在linux的spi.iic.usb等复杂驱动里广泛使用.后面几节分别对这些思想进行详细说明. 思想1:驱动与设备分离 ...
- 七、设备驱动中的阻塞与非阻塞 IO(二)
7.2 轮询 7.2.1 介绍 在用户程序中的 select() 和 poll() 函数最终会使设备驱动中的 poll() 函数被执行. 设备驱动程序中的轮询函数原型: /** 用于询问设备是否可以非 ...
- 七、设备驱动中的阻塞与非阻塞 IO(一)
7.1 阻塞与非阻塞 IO 阻塞操作是指在执行设备操作的时候,若不能获取资源,则挂起进程直到满足可操作的条件后再进行操作.被挂起的进程进入睡眠状态,被从调度器的运行队列移走,直到等待的条件被满足. 非 ...
- Linux字符设备驱动框架
字符设备是Linux三大设备之一(另外两种是块设备,网络设备),字符设备就是字节流形式通讯的I/O设备,绝大部分设备都是字符设备,常见的字符设备包括鼠标.键盘.显示器.串口等等,当我们执行ls -l ...
- 《连载 | 物联网框架ServerSuperIO教程》- 3.设备驱动介绍
1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架Serve ...
随机推荐
- Bind Enum to ListControl
当使用MVVM时,相信你和我一样经常有这样的需求: 在ViewModel里定义了一个Enum,它必然是对应UI上的一个ListControl作为不同选项. 有一种做法是使用Converter,将Enu ...
- wpf单实例运行
默认情况下我们可以打开一个应用程序多个实例,例如你双击一个exe多次.当然有些时候这么做会带来很多好处,但是有时我们又不希望这么做,要避免这个问题其实很简单,同WinForm中单实例运行一个应用是一样 ...
- Educational Codeforces Round 8 D. Magic Numbers
Magic Numbers 题意:给定长度不超过2000的a,b;问有多少个x(a<=x<=b)使得x的偶数位为d,奇数位不为d;且要是m的倍数,结果mod 1e9+7; 直接数位DP;前 ...
- spring ioc aop 原理
spring ioc aop 原理 spring ioc aop 的原理 spring的IoC容器是spring的核心,spring AOP是spring框架的重要组成部分. 在传统的程序设计中,当调 ...
- 关于http客户端常见错误"警告:Going to buffer response body of large or unknown size. Using getResponseBodyAsStream instead is rec"
在开发过程中,经常得写http客户端测试接口服务,今天在使用过程中出现了这样的一个警告: 警告: Going to buffer response body of large or unknown s ...
- Java连接MySQl数据库实现代码
1. 获取数据库连接和查询代码 package connectionmysql; import java.sql.Connection; import java.sql.DriverManager; ...
- JS获取IP、MAC和主机名的几种方法
方法一(只针对IE且客户端的IE允许AcitiveX运行,通过平台:XP,SERVER03,2000): 获取客户端IP. <HTML> <HEAD> <TITLE> ...
- jquery mobile event
jquery.js $(document).on("mobileinit", function() { // }); jquery.mobile.js $(document).re ...
- SQL中Case的使用方法(上篇)(转)
http://www.cnblogs.com/fxgachiever/archive/2010/09/09/1822106.html Case具有两种格式.简单Case函数和Case搜索函数. --简 ...
- Oracle11g密码区分大小写导致database link无法连接
http://f.dataguru.cn/thread-128013-1-1.html Oracle11g的密码默认是区分大小写的,该特性通过初始化参数sec_case_sensitive_logon ...