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; //异步结构体指针,用于读
};
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;
}