这里主要是例子,分析在另一篇博文,下面是支持阻塞操作的设备驱动代码
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define GLOBALMEM_SIZE 0x1000
#define MEM_CLEAR 0x1
#define GLOBALMEM_MAJOR 250
static struct class *firstdrv_class;//自动建立设备节点时要用到
static int globalmem_major = GLOBALMEM_MAJOR;
//设备结构体
struct globalmem_dev{
struct cdev cdev;
unsigned int current_len;//fifo有效长度
unsigned char mem[GLOBALMEM_SIZE];
struct semaphore sem;//并发控制用到的信号量
wait_queue_head_t r_wait;//阻塞读用到的等待队列头
wait_queue_head_t w_wait;;//阻塞写用到的等待队列头
};
struct globalmem_dev *globalmem_devp;
int globalmem_open(struct inode *inode,struct file *filp)
{
filp->private_data =
globalmem_devp;//将设备结构体指针赋给文件私有数据指针
return 0;
}
int globalmem_release(struct inode *inode,struct file *filp)
{
return 0;
}
static int globalmem_ioctl(struct inode *inodeep,struct file
*filp,unsigned int cmd,unsigned long arg)
{
struct globalmem_dev *dev =
filp->private_data;
switch (cmd) {
case MEM_CLEAR :
if
(down_interruptible(&dev->sem))//获得信号量
return -ERESTARTSYS;//如果返回非0说明进程在等待时被信号打断
memset(dev->mem,0,GLOBALMEM_SIZE);//清除全局内存
up(&dev->sem);//释放信号量
printk(KERN_INFO "globalmem is
set to zero\n");
break;
default :
return -EINVAL;
}
return 0;
}
static ssize_t globalmem_read(struct file *filp,char __user
*buf,size_t count,loff_t *ppos)
{
int ret;
struct globalmem_dev *dev =
filp->private_data;//获得设备结构体指针
DECLARE_WAITQUEUE(wait,current);//定义等待队列并初始化
down(&dev->sem);//获得信号
add_wait_queue(&dev->r_wait,&wait);//把队列加到wait队列头去
while(dev->current_len == 0){//如果数据缓冲区为空,表示不可读
if(filp->f_flags
&O_NONBLOCK){//如果以非阻塞打开
ret = -EAGAIN;
goto out;//释放信号
}
__set_current_state(TASK_INTERRUPTIBLE);//改变进程状态为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)){
return -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)
courrent_len:%d\n",count,dev->current_len);
printk(KERN_INFO "mydyy TQ2440\n");
wake_up_interruptible(&dev->w_wait);//唤醒写等待队列
ret = count;
}
out: up(&dev->sem);
out2:remove_wait_queue(&dev->r_wait,&wait);//移除等待队列
set_current_state(TASK_RUNNING);//改变进程状态为TASK_RUNNING
return ret;
}
static ssize_t globalmem_write(struct file *filp,const char
__user *buf,size_t count,loff_t *ppos)
{
int ret;
struct globalmem_dev *dev =
filp->private_data;//获得设备结构体指针
DECLARE_WAITQUEUE(wait,current);//定义并初始化写等待队列
down(&dev->sem);//获得信号
add_wait_queue(&dev->w_wait,&wait);//把写等待队列加到队列头
while(dev->current_len ==
GLOBALMEM_SIZE){//如果fifo已满
if(filp->f_flags &
O_NONBLOCK){//以非阻塞方式打开
ret = -EAGAIN;
goto out;//释放信号
}
__set_current_state(TASK_INTERRUPTIBLE);//改变进程状态为TASK_INTERRUPTIBLE
up(&dev->sem);//释放信号
schedule();//调度其它进程
if(signal_pending(current)){//如果是信号打断
ret = -ERESTARTSYS;
goto
out2;//主要是把队列从队列头中拉出,并改变进程状态
}
// down(&dev->sem);
}
if(count > GLOBALMEM_SIZE -
dev->current_len)
count = GLOBALMEM_SIZE - dev->current_len;
if(copy_from_user(dev->mem +
dev->current_len,buf,count)){
return -EFAULT;
goto out;
}else {
dev->current_len += count;//有效长度变长
printk(KERN_INFO "write %d bytes(s) current_len
%d\n",count,dev->current_len);
printk(KERN_INFO "mycgy TQ2440\n");
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);//改变进程状态为TASK_RUNNING
return ret;
}
static loff_t globalmem_llseek(struct file *filp,loff_t
offset,int orig)
{
loff_t ret = 0;
switch(orig){
case 0 :
if(offset <
0){
ret =
-EINVAL;
break;
}
if((unsigned int)offset > GLOBALMEM_SIZE){
ret =
-EINVAL;
break;
}
filp->f_pos = (unsigned int)offset;
ret = filp->f_pos;
break;
case 1 :
if((filp->f_pos + offset)
> GLOBALMEM_SIZE){
ret = -EINVAL;
break;
}
if((filp->f_pos + offset)
< 0){
ret = -EINVAL;
break;
}
filp->f_pos += offset;
break;
default :
ret = -EINVAL;
break;
}
return ret;
}
static const struct file_operations globalmem_fops = {
.owner = THIS_MODULE,
.llseek = globalmem_llseek,
.read = globalmem_read,
.write = globalmem_write,
.ioctl = globalmem_ioctl,
.open = globalmem_open,
.release = globalmem_release,
};
static void globalmem_setup_cdev(struct globalmem_dev *dev,int
index)
{
int err,devno = MKDEV(globalmem_major,index);
cdev_init(&dev->cdev,&globalmem_fops);
dev->cdev.owner = THIS_MODULE;
err =
cdev_add(&dev->cdev,devno,1);
if(err)
printk(KERN_NOTICE "Error %d adding globalmem
%d",err,index);
}
int globalmem_init(void)
{
int result;
dev_t devno = MKDEV(globalmem_major,0);
if(globalmem_major)
result = register_chrdev_region(devno, 1,
"globalmem");
else{
result =
alloc_chrdev_region(&devno,0,1,"globalmem");
globalmem_major = MAJOR(devno);
}
if (result < 0)
return result;
firstdrv_class = class_create(THIS_MODULE,
"firstdrv");//建立firstdrv这个类
device_create(firstdrv_class,NULL,devno,NULL,"globalmem");//自动创建设备节点/dev/LED
globalmem_devp = kmalloc(sizeof(struct
globalmem_dev),GFP_KERNEL);
if(!globalmem_devp){
result = -ENOMEM;
goto fail_malloc;
}
memset(globalmem_devp,0,sizeof(struct globalmem_dev));
globalmem_setup_cdev(globalmem_devp,0);
init_MUTEX(&globalmem_devp->sem);//定义并初始化一个互斥信号
init_waitqueue_head(&globalmem_devp->r_wait);//初始化读队列头
init_waitqueue_head(&globalmem_devp->w_wait);//初始化写队列头
//globalmem_devp->current_len = 0x800;
return 0;
fail_malloc:
unregister_chrdev_region(devno,1);
return result;
}
void globalmem_exit(void)
{
cdev_del(&globalmem_devp->cdev);
kfree(globalmem_devp);
device_destroy(firstdrv_class,MKDEV(globalmem_major,0));
class_destroy(firstdrv_class);//释放这个类,干掉它
unregister_chrdev_region(MKDEV(globalmem_major,0),1);
}
MODULE_LICENSE("Dual BSD/GPL");
module_init(globalmem_init);
module_exit(globalmem_exit);
上面是驱动程序,下面给出运行时现象(用的是TQ2440开发板和linux-2.6.30.4内核)
[root@cgyl2010 ~]#
[root@cgyl2010 ~]#insmod yueyi.ko
[root@cgyl2010 ~]#cat /dev/globalmem &
[root@cgyl2010 ~]#echo "yygy" > /dev/globalmem
write 5 bytes(s) current_len 5
mycgy TQ2440
read 5 bytes(s) courrent_len:0
mydyy TQ2440
yygy
[root@cgyl2010 ~]#
顺便给出Makefile文件
obj-m += yueyi.o
KERDIR = /work/linux-2.6.30.4
#KERDIR = /usr/src/linux-headers-2.6.32-33-generic
modules:
make -C $(KERDIR) M=`pwd` modules
clean:
make -C $(KERDIR) M=`pwd` clean
##################################################################################################
下面是支持轮询的驱动,和上面的差不多
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define GLOBALMEM_SIZE 0x1000
#define MEM_CLEAR 0x1
#define GLOBALMEM_MAJOR 250
static struct class *firstdrv_class;
static int globalmem_major = GLOBALMEM_MAJOR;
struct globalmem_dev{
struct cdev cdev;
unsigned int current_len;
unsigned char mem[GLOBALMEM_SIZE];
struct semaphore sem;
wait_queue_head_t r_wait;
wait_queue_head_t w_wait;
};
struct globalmem_dev *globalmem_devp;
int globalmem_open(struct inode *inode,struct file *filp)
{
filp->private_data = globalmem_devp;
return 0;
}
int globalmem_release(struct inode *inode,struct file *filp)
{
return 0;
}
static int globalmem_ioctl(struct inode *inodeep,struct file
*filp,unsigned int cmd,unsigned long arg)
{
struct globalmem_dev *dev =
filp->private_data;
switch (cmd) {
case MEM_CLEAR :
if
(down_interruptible(&dev->sem))
return -ERESTARTSYS;
memset(dev->mem,0,GLOBALMEM_SIZE);
up(&dev->sem);
printk(KERN_INFO "globalmem is
set to zero\n");
break;
default :
return -EINVAL;
}
return 0;
}
static ssize_t globalmem_read(struct file *filp,char __user
*buf,size_t count,loff_t *ppos)
{
int ret;
struct globalmem_dev *dev =
filp->private_data;
DECLARE_WAITQUEUE(wait,current);
down(&dev->sem);
add_wait_queue(&dev->r_wait,&wait);
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)){
return -EFAULT;
goto out;
}else {
memcpy(dev->mem,dev->mem +
count,dev->current_len - count);
dev->current_len -= count;
printk(KERN_INFO "read %d bytes(s)
courrent_len:%d\n",count,dev->current_len);
printk(KERN_INFO "mydyy TQ2440\n");
wake_up_interruptible(&dev->w_wait);
ret = count;
}
out: up(&dev->sem);
out2:remove_wait_queue(&dev->r_wait,&wait);
set_current_state(TASK_RUNNING);
return ret;
}
static ssize_t globalmem_write(struct file *filp,const char
__user *buf,size_t count,loff_t *ppos)
{
int ret;
struct globalmem_dev *dev =
filp->private_data;
DECLARE_WAITQUEUE(wait,current);
down(&dev->sem);
add_wait_queue(&dev->w_wait,&wait);
while(dev->current_len == GLOBALMEM_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 > GLOBALMEM_SIZE -
dev->current_len)
count = GLOBALMEM_SIZE - dev->current_len;
if(copy_from_user(dev->mem +
dev->current_len,buf,count)){
return -EFAULT;
goto out;
}else {
dev->current_len += count;
printk(KERN_INFO "write %d bytes(s) current_len
%d\n",count,dev->current_len);
printk(KERN_INFO "mycgy TQ2440\n");
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;
}
static loff_t globalmem_llseek(struct file *filp,loff_t
offset,int orig)
{
loff_t ret = 0;
switch(orig){
case 0 :
if(offset <
0){
ret =
-EINVAL;
break;
}
if((unsigned int)offset > GLOBALMEM_SIZE){
ret =
-EINVAL;
break;
}
filp->f_pos = (unsigned int)offset;
ret = filp->f_pos;
break;
case 1 :
if((filp->f_pos + offset)
> GLOBALMEM_SIZE){
ret = -EINVAL;
break;
}
if((filp->f_pos + offset)
< 0){
ret = -EINVAL;
break;
}
filp->f_pos += offset;
break;
default :
ret = -EINVAL;
break;
}
return ret;
}
static unsigned int globalfifo_poll(struct file *filp, poll_table
*wait)
{
unsigned int mask = 0;
struct globalmem_dev *dev =
filp->private_data;//获得设备结构体指针
down(&dev->sem);//获取信号
poll_wait(filp,&dev->r_wait,wait);//把读队列头加到poll_table结构所指的链表中去
poll_wait(filp,&dev->w_wait,wait);//把写队列头加到poll_table结构所指的链表中去
if(dev->current_len != 0)//fifo非空
mask |= POLLIN | POLLRDNORM;//标志数据可获得
if(dev->current_len != GLOBALMEM_SIZE)//fifo非满
mask |= POLLOUT | POLLWRNORM;//标志数据可写入
up(&dev->sem);//释放信号
return mask;//返回标志mask
}
static const struct file_operations globalmem_fops = {
.owner = THIS_MODULE,
.llseek = globalmem_llseek,
.read = globalmem_read,
.write = globalmem_write,
.ioctl = globalmem_ioctl,
.poll = globalfifo_poll,
.open = globalmem_open,
.release = globalmem_release,
};
static void globalmem_setup_cdev(struct globalmem_dev *dev,int
index)
{
int err,devno = MKDEV(globalmem_major,index);
cdev_init(&dev->cdev,&globalmem_fops);
dev->cdev.owner = THIS_MODULE;
err =
cdev_add(&dev->cdev,devno,1);
if(err)
printk(KERN_NOTICE "Error %d adding globalmem
%d",err,index);
}
int globalmem_init(void)
{
int result;
dev_t devno = MKDEV(globalmem_major,0);
if(globalmem_major)
result = register_chrdev_region(devno, 1,
"globalmem");
else{
result =
alloc_chrdev_region(&devno,0,1,"globalmem");
globalmem_major = MAJOR(devno);
}
if (result < 0)
return result;
firstdrv_class = class_create(THIS_MODULE,
"firstdrv");//建立firstdrv这个类
device_create(firstdrv_class,NULL,devno,NULL,"globalmem");//自动创建设备节点/dev/LED
globalmem_devp = kmalloc(sizeof(struct
globalmem_dev),GFP_KERNEL);
if(!globalmem_devp){
result = -ENOMEM;
goto fail_malloc;
}
memset(globalmem_devp,0,sizeof(struct globalmem_dev));
globalmem_setup_cdev(globalmem_devp,0);
init_MUTEX(&globalmem_devp->sem);
init_waitqueue_head(&globalmem_devp->r_wait);
init_waitqueue_head(&globalmem_devp->w_wait);
return 0;
fail_malloc:
unregister_chrdev_region(devno,1);
return result;
}
void globalmem_exit(void)
{
cdev_del(&globalmem_devp->cdev);
kfree(globalmem_devp);
device_destroy(firstdrv_class,MKDEV(globalmem_major,0));
class_destroy(firstdrv_class);//释放这个类,干掉它
unregister_chrdev_region(MKDEV(globalmem_major,0),1);
}
MODULE_LICENSE("Dual BSD/GPL");
module_init(globalmem_init);
module_exit(globalmem_exit);
下面给出应用程序(用到poll)
#include
#include
#include
#include
#include
#define FIFO_CLEAR 0x1
#define BUFFER_LEN 20//用于清除内存
main()
{
int fd,num;
char rd_ch[BUFFER_LEN];
fd_set rfds,wfds;//读/写文件描述符集
fd = open("/dev/globalmem",O_RDWR |
O_NONBLOCK);以非阻塞可读写方式打开
if(fd != -1){
if(ioctl(fd,FIFO_CLEAR,0) <
0)//先清空整个fifo
printf("ioctl command
failed\n");
while (1) {
FD_ZERO(&rfds);//清除读文件描述符集
FD_ZERO(&wfds);//清除写文件描述符集
FD_SET(fd,&rfds);//把fd文件描述符加到读文件描述符集中去
FD_SET(fd,&wfds);//把fd文件描述符加到写文件描述符集中去
select(fd +
1,&rfds,&wfds,NULL,NULL);//系统调用最终会调用到驱动里的poll()函数
if(FD_ISSET(fd,&rfds))//判断读文件描述符是否置为,也判断就是说是否可读
printf("poll
monitor:cat be read\n");
sleep(1);//延时1秒
if(FD_ISSET(fd,&wfds))//判断写文件描述符是否置为,也判断就是说是否可写
printf("poll
monitor:cat be writen\n");
sleep(1);
}
}else{
printf("Device open
failure\n");
}
}
下面是运行时现象(开发板TQ2440,内核linux-2.6.30.4)
[root@cgyl2010 ~]#
[root@cgyl2010 ~]#insmod yg.ko
[root@cgyl2010 ~]#./cs &
globalmem is set to zero
[root@cgyl2010 ~]#poll monitor:cat be writen
poll monitor:cat be writen
[root@cgyl2010 ~]#poll monitor:cat be writen
poll monitor:cat be writen
ecpoll monitor:cat be writen
ho poll monitor:cat be writen
dpoll monitor:cat be writen
yycgypoll monitor:cat be writen
> /depoll monitor:cat be
writen
[root@cgyl2010 ~]#echo dyycgy > /dev/globalmem poll
monitor:cat be writen
write 7 bytes(s) current_len 7
mycgy TQ2440
[root@cgyl2010 ~]#poll monitor:cat be read
poll monitor:cat be writen
poll monitor:cat be read
poll monitor:cat be writen
poll monitor:cat be read
poll monitor:cat be writen
......