觉得还是贴代码最直接,以后要用的时候也方便参考。
先是相应驱动的详细代码:
/* linux/drivers/char/sep4020_char/sep4020_fifo.c
*
*
Copyright (c) 2009 leeming
*
* sep4020 fifo driver.
*
*
Changelog:
* 12-Aug-2009 leeming Initial version
*
* This
program is free software; you can redistribute it and/or modify
* it
under the terms of the GNU General Public License as published by
*
the Free Software Foundation; either version 2 of the License, or
*
(at your option) any later version.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define FIFO_MAJOR 252 //主设备号
#define
MAX_FIFO_BUF 16 //按键缓冲区的大小
struct fifodev
{
unsigned
char buf[MAX_FIFO_BUF]; //按键缓冲区
unsigned int current_len;
wait_queue_head_t
r_wait; //读等待队列
wait_queue_head_t w_wait;
//写等待队列
struct cdev cdev;
} ;
struct fifodev
*fifo_dev; //键盘结构体
//返回读到的字节数
static ssize_t
sep4020_fifo_read(struct file *filp, char __user *buf, size_t size,
loff_t *ppos)
{
int ret;
// 第
二个参数condition必须满足,否则阻塞
wait_event_interruptible(fifo_dev->r_wait,
fifo_dev->current_len != 0);
if(size >
fifo_dev->current_len)
size = fifo_dev->current_len;
if(copy_to_user(buf,
fifo_dev->buf, size))
{
ret = -EFAULT;
goto out;
}
else
{
memcpy(fifo_dev->buf,
fifo_dev->buf+size, fifo_dev->current_len-size);
fifo_dev->current_len
= fifo_dev->current_len - size;
wake_up_interruptible(&fifo_dev->w_wait);
ret
= size;
}
out:
return ret;
}
//返回写入的字节数
static ssize_t sep4020_fifo_write(struct file *filp,
const char __user *buf, size_t size, loff_t *ppos)
{
int ret;
wait_event_interruptible(fifo_dev->r_wait,
fifo_dev->current_len != MAX_FIFO_BUF);
if(size >
(MAX_FIFO_BUF-fifo_dev->current_len))
{
size =
MAX_FIFO_BUF-fifo_dev->current_len;
}
if(copy_from_user(fifo_dev->buf+fifo_dev->current_len,
buf, size))
{
ret = -EFAULT;
goto out;
}
else
{
fifo_dev->current_len
+= size;
ret = size;
}
wake_up_interruptible(&fifo_dev->r_wait);
out:
return
ret;
}
//在使用echo或者cat的时候都会调用open函数
static int
sep4020_fifo_open(struct inode *inode, struct file *filp)
{
//memset(fifo_dev->buf,
0, MAX_FIFO_BUF);
//fifo_dev->current_len = 0;
return 0;
}
static int sep4020_fifo_release(struct inode *inode,
struct file *filp)
{
return 0;
}
/*
#define POLLIN 0x0001
//有数据可以读入,read不会阻塞,注意:select的请情况下,即使到EOF也是ready的.
#define
POLLPRI 0x0002 //紧急数据,比如TCP,或者packet模式的peseudo-terminal发现slave的状态有变化.
#define
POLLOUT 0x0004 //写入不会阻塞.
#define POLLERR 0x0008 //输出出错
#define
POLLHUP 0x0010 //Hang up (output only).
#define
POLLNVAL 0x0020 //Invalid request: fd not open (output only).
The rest seem to be more-or-less nonstandard. Check
them!
#define POLLRDNORM 0x0040 //POLLIN.
#define
POLLRDBAND 0x0080 //高优先级的数据read for read (generally unused on Linux).
#define
POLLWRNORM 0x0100 //Equivalent to POLLOUT.
#define
POLLWRBAND 0x0200 //Priority data may be written.
#define
POLLMSG 0x0400
#define POLLREMOVE 0x1000
*/
static unsigned int sep4020_fifo_poll(struct
file *filp, poll_table *wait)
{
unsigned int mask = 0;
//加入这两句
话是为了在读写状态发生变化的时候,通知核心层,让核心层重新调用poll函数查询信息。也就是说这两句只会在select阻塞的时候用到
//当
利用select函数发现既不能读又不能写时,select函数会阻塞,但是此时的阻塞并不是轮询,而是睡眠,通过下面两个队列发生变化时通知
select
poll_wait(filp, &fifo_dev->r_wait, wait);
poll_wait(filp,
&fifo_dev->w_wait, wait);
if(fifo_dev->current_len
!= 0)
{
mask |= POLLIN | POLLRDNORM;//可读,同时写上POLLRDNORM
}
if(fifo_dev->current_len != MAX_FIFO_BUF)
{
mask
|= POLLOUT | POLLWRNORM;//可写,同时写上POLLWRNORM
}
return
mask;
}
static struct file_operations sep4020_fifo_fops =
{
.owner
= THIS_MODULE,
.read = sep4020_fifo_read,
.write =
sep4020_fifo_write,
.poll = sep4020_fifo_poll,
.open =
sep4020_fifo_open,
.release = sep4020_fifo_release,
};
static int __init sep4020_fifo_init(void)
{
int
err,result;
dev_t devno;
devno = MKDEV(FIFO_MAJOR, 0);
result =
register_chrdev_region(devno, 1, "sep4020_fifo"); //向系统静态申请设备号
if (result < 0)
{
return result;
}
fifo_dev
= kmalloc(sizeof(struct fifodev), GFP_KERNEL);
if (fifo_dev ==
NULL)
{
result = -ENOMEM;
unregister_chrdev_region(devno,
1);
return result;
}
memset(fifo_dev,0,sizeof(struct
fifodev)); //初始化
cdev_init(&fifo_dev->cdev,
&sep4020_fifo_fops);
fifo_dev->cdev.owner = THIS_MODULE;
//初始化等待对列
init_waitqueue_head(&fifo_dev->r_wait);
init_waitqueue_head(&fifo_dev->w_wait);
//
向系统注册该字符设备
err = cdev_add(&fifo_dev->cdev, devno, 1);
if
(err)
{
printk("fifo adding err\r\n");
unregister_chrdev_region(devno,1);
kfree(fifo_dev);
return
err;
}
return 0;
}
static void __exit sep4020_fifo_exit(void)
{
cdev_del(&fifo_dev->cdev);
kfree(fifo_dev);
unregister_chrdev_region(MKDEV(FIFO_MAJOR,
0), 1);
}
module_init(sep4020_fifo_init);
module_exit(sep4020_fifo_exit);
MODULE_AUTHOR("Leeming Zhang");
MODULE_LICENSE("GPL");
/*****************************************************************************************************************/
接下来是相应的应用程序:
#include
#include
#include
#include
int main(int argc, char **argv)
{
int fd;
char
buf[16];
fd_set rfds,wfds; //读写描述符集合
unsigned char
w_buf[7] = {'a','b','c','d','e','f','g'};
//open的标志位有:O_RDONLY
O_WRONLY O_RDWR O_NONBLOCK O_NDELAY O_SYNC O_NOCTY
fd =
open("/dev/fifo",O_RDWR);
if(fd == -1)
{
printf("wrong\r\n");
exit(-1);
}
while(1)
{
//
初始化文件描述符集合
//清零
FD_ZERO(&rfds);
FD_ZERO(&wfds);
//
将文件描述符加入文件描述符集合中,利用函数FD_CLR(int fd,fd_set *set)将一个文件描述符从文件描述符集中清除
FD_SET(fd,
&rfds);
FD_SET(fd, &wfds);
//函数原型:int select(int
numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct
timeval *timeout);
//readfds writefds
exceptfds分别是被select监视的读,写和异常处理的文件描述符集合,numfds是需要检查的文件描述符加1。
select(fd+1,
&rfds, &wfds, NULL, NULL);
//判断是否被置位,通过select函数调用驱动
poll函数的返回值,来判断是否可读,还是可写,还是又能读又能写;当然如果驱动又不能读又不能写,在select那儿会阻塞,直到能读或者能写为止
if(FD_ISSET(fd,
&rfds))
{
printf("Poll monitor: can be read\n");
}
if(FD_ISSET(fd,
&wfds))
{
printf("Poll monitor: can be written\n");
}
}
return 0;
}