`
ydbc
  • 浏览: 719509 次
  • 性别: Icon_minigender_1
  • 来自: 大连
文章分类
社区版块
存档分类
最新评论

Linux驱动:阻塞式读写测试

 
阅读更多

Linux驱动:阻塞式读写测试


本文博客链接:http://blog.csdn.net/jdh99,作者:jdh,转载请注明.

环境:

主机:Fedora12

目标板:MINI6410

目标板LINUX内核版本:2.6.38


实现功能:

开辟一个256字节的循环缓冲,可以同时读写,如果数据量不足,则读进程会被阻塞,直到有数据写入


驱动源代码:

test_driver.c:

#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
//#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
#include <linux/major.h>

#include <mach/map.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>

#include <plat/gpio-cfg.h>
#include <mach/gpio-bank-e.h>
#include <mach/gpio-bank-k.h>
#include <mach/gpio-bank-h.h>
#include <mach/gpio-bank-n.h>
#include <mach/gpio-bank-l.h>
#include <mach/gpio-bank-p.h>

#include <linux/device.h>

#include <linux/jiffies.h>
#include <linux/string.h>

#include <linux/semaphore.h>
#include <linux/sched.h> 
#include <linux/wait.h>

#define DEVICE_NAME "test_driver"
#define T_MAJORS	800

//设备结构
static struct _Test_Driver_Device
{
	struct cdev fun_cdev;
	//定义缓冲
	unsigned char *buffer,*end;
	//读写指针
	unsigned char *rp,*wp;
	//读信号量
	struct semaphore sem_r;
	//写信号量
	struct semaphore sem_w;
	//等待队列头
	struct wait_queue_head_t *wq;
};
struct _Test_Driver_Device *Test_Driver_Device;

static dev_t dev;
static struct class    *test_class;

//开辟缓存,用来读写
#define LEN_BUF	256
static unsigned char Buffer[LEN_BUF];

//功能:初始化缓存
static void init_buf(void)
{
	memset(Buffer,0,LEN_BUF);
}

//功能:读取缓存
//返回:读取的字节数
ssize_t test_driver_read(struct file *filp,char __user *buf,size_t count,loff_t *f_pos)
{
	int temp1 = 0,temp2 = 0,temp3 = 0;

	//获取信号量
	if (down_interruptible(&Test_Driver_Device->sem_r))
	{
		return -ERESTARTSYS;
	}
	
	//循环防止解除阻塞时的竞争
	while (Test_Driver_Device->wp == Test_Driver_Device->rp)
	{
		//读不到数据
		//释放信号量
		up(&Test_Driver_Device->sem_r);

		//判断是否是非阻塞读
		if (filp->f_flags & O_NONBLOCK)
		{
			return -EAGAIN;
		}
		
		//如果是阻塞式读,则阻塞
		if (wait_event_interruptible(Test_Driver_Device->wq,Test_Driver_Device->wp != Test_Driver_Device->rp))
		{
			return -ERESTARTSYS;
		}

		//获取信号量
		if (down_interruptible(&Test_Driver_Device->sem_r))
		{
			return -ERESTARTSYS;
		}
	}

	if (Test_Driver_Device->wp > Test_Driver_Device->rp)
	{
		count = min(count,(size_t)(Test_Driver_Device->wp - Test_Driver_Device->rp));
		//拷贝数据到用户空间
		if (copy_to_user(buf,Test_Driver_Device->rp,count))
		{
			return -EFAULT;
		}
		Test_Driver_Device->rp += count;
	}
	else
	{
		temp1 = Test_Driver_Device->end - Test_Driver_Device->rp + 1;
		temp2 = Test_Driver_Device->wp - Test_Driver_Device->buffer;
		if (count <= temp1)
		{
			//拷贝数据到用户空间
			if (copy_to_user(buf,Test_Driver_Device->rp,count))
			{
				return -EFAULT;
			}
			Test_Driver_Device->rp += count;
		}
		else
		{
			//拷贝数据到用户空间
			if (copy_to_user(buf,Test_Driver_Device->rp,temp1))
			{
				return -EFAULT;
			}
			Test_Driver_Device->rp = Test_Driver_Device->buffer;
			temp3 = min(count - temp1,temp2);
			count = temp1 + temp3;
			if (copy_to_user(buf + temp1,Test_Driver_Device->rp,temp3))
			{
				return -EFAULT;
			}
			Test_Driver_Device->rp += temp3;
		}
	}

	if (Test_Driver_Device->rp == Test_Driver_Device->end + 1)
	{
		Test_Driver_Device->rp = Test_Driver_Device->buffer;
	}
	//释放信号量
	up(&Test_Driver_Device->sem_r);
	printk (DEVICE_NAME"\tjdh:rp zhi zhen = %d\n",Test_Driver_Device->rp - Test_Driver_Device->buffer);

	return count;
}

//功能:写入缓存
//返回:写入的字节数
ssize_t test_driver_write(struct file *filp,const char __user *buf,size_t count,loff_t *f_pos)
{
	int temp1 = 0,temp2 = 0;;

	//判断需要写入的字节数是否大于缓存
	if (count > LEN_BUF)
	{
		return -ENOMEM;
	}

	//获取信号量
	if (down_interruptible(&Test_Driver_Device->sem_w))
	{
		return -ERESTARTSYS;
	}

	//写入缓存
	if (count <= (Test_Driver_Device->end - Test_Driver_Device->wp + 1))
	{
		//从用户空间拷贝数据
		if (copy_from_user(Test_Driver_Device->wp,buf,count))
		{
			return -EFAULT;
		}
		Test_Driver_Device->wp += count;
	}
	else
	{
		temp1 = Test_Driver_Device->end - Test_Driver_Device->wp + 1;
		temp2 = count - temp1;
		//从用户空间拷贝数据
		if (copy_from_user(Test_Driver_Device->wp,buf,temp1))
		{
			return -EFAULT;
		}
		Test_Driver_Device->wp = Test_Driver_Device->buffer;
		//从用户空间拷贝数据
		if (copy_from_user(Test_Driver_Device->wp,buf + temp1,temp2))
		{
			return -EFAULT;
		}
		Test_Driver_Device->wp += temp2;
	}

	if (Test_Driver_Device->wp == Test_Driver_Device->end + 1)
	{
		Test_Driver_Device->wp = Test_Driver_Device->buffer;
	}
	//唤醒阻塞进程
	wake_up_interruptible(&Test_Driver_Device->wq);
	//释放信号量
	up(&Test_Driver_Device->sem_w);
	printk (DEVICE_NAME"\tjdh:wp zhi zhen = %d\n",Test_Driver_Device->wp - Test_Driver_Device->buffer);

	return count;
}

static struct file_operations io_dev_fops = {
	.owner = THIS_MODULE,
	.write = test_driver_write,
	.read = test_driver_read,
};

static int __init dev_init(void)
{
	int ret;
	unsigned temp;

	init_buf();

	//分配结构体
	Test_Driver_Device = kmalloc(sizeof(struct _Test_Driver_Device),GFP_KERNEL);
	if (!Test_Driver_Device)
	{
		unregister_chrdev_region(dev,1);
    		device_destroy(test_class, dev); 
    		class_destroy(test_class);

		return -ENOMEM;
	}
	
	//定义缓冲的开始和结束的指针
	Test_Driver_Device->buffer = Buffer;
	Test_Driver_Device->end = Buffer + LEN_BUF - 1;
	Test_Driver_Device->rp = Test_Driver_Device->buffer;
	Test_Driver_Device->wp = Test_Driver_Device->buffer;
	//初始化读信号量
	sema_init(&Test_Driver_Device->sem_r,1);
	//初始化写信号量
	sema_init(&Test_Driver_Device->sem_w,1);
	//初始化等待队列头
	init_waitqueue_head(&Test_Driver_Device->wq);

	dev = MKDEV(T_MAJORS,0);
	cdev_init(&Test_Driver_Device->fun_cdev,&io_dev_fops);
	ret = register_chrdev_region(dev,1,DEVICE_NAME);
	if (ret < 0) return 0;
	ret = cdev_add(&Test_Driver_Device->fun_cdev,dev,1);
	if (ret < 0) return 0;

	printk (DEVICE_NAME"\tjdh:test_driver initialized!!\n");

	test_class = class_create(THIS_MODULE, "test_class1"); 
	if (IS_ERR(test_class)) 
	{ 
		printk(KERN_INFO "create class error\n"); 
		return -1; 
	} 
	device_create(test_class, NULL, dev, NULL, "test_driver"); 

	return ret;
}

static void __exit dev_exit(void)
{
	unregister_chrdev_region(dev,1);

    	device_destroy(test_class, dev); 
    	class_destroy(test_class);
}

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("JDH");


用户程序:

分为写程序,和读程序。写程序每次写入100字节,读每次读取50字节。

test_driver_write.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "stdio.h"
#include "sys/types.h"
#include "sys/ioctl.h"
#include "stdlib.h"
#include "termios.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "sys/time.h"

#include <string.h>  
#include <stdlib.h> 

int main(int argc, char** argv)
{
	int fd;
	unsigned char buf[256];
	int i = 0;
	int err = 0;

	fd = open("/dev/test_driver",O_RDWR);
	if (fd < 0)
	{
		perror("open test_driver error");
		exit(1);
	}
	
	//写入数据
	for (i = 0;i < 256;i++)
	{
		buf[i] = i;
	}
	err = write(fd,buf,100);
	printf("write %d\n",err);
	perror("open 22 error");

	close(fd);
	
	return 0;
}


test_driver_read.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "stdio.h"
#include "sys/types.h"
#include "sys/ioctl.h"
#include "stdlib.h"
#include "termios.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "sys/time.h"

#include <string.h>  
#include <stdlib.h> 

int main(int argc, char** argv)
{
	int fd;
	unsigned char temp[256];
	int i = 0;
	int err = 0;

	fd = open("/dev/test_driver",O_RDWR);
	if (fd < 0)
	{
		perror("open test_driver error");
		exit(1);
	}

	//读取数据
	err = read(fd,temp,50);
	printf("read %d\n",err);
	perror("open 33error");

	//输出
	for (i = 0;i < 50;i++)
	{
		printf("%d\t",(int)temp[i]);
	}

	close(fd);
	
	return 0;
}


测试说明:

将编译所得的可执行文件test_driver_write和test_driver_read上传到开发板

同时运行,测试读写





分享到:
评论

相关推荐

    Linux下支持阻塞操作的字符设备驱动

    Linux下支持阻塞操作的字符设备驱动Linux下支持阻塞操作的字符设备驱动Linux下支持阻塞操作的字符设备驱动Linux下支持阻塞操作的字符设备驱动Linux下支持阻塞操作的字符设备驱动Linux下支持阻塞操作的字符设备驱动...

    嵌入式Linux驱动程序设计从入门到精通.part01.rar

    2 阻塞式读写 3 定时器 4 内存映射 5 /proc访问 6 工作队列 CHA3 LED.c LED灯驱动 button.c 键盘驱动 CHA4 Linux内核I2C驱动 CHA5 usb-skeleton.c USB驱动框架 camera Spcaview相关代码 CHA6 skeleton ...

    嵌入式Linux驱动程序设计从入门到精通.part02.rar

    2 阻塞式读写 3 定时器 4 内存映射 5 /proc访问 6 工作队列 CHA3 LED.c LED灯驱动 button.c 键盘驱动 CHA4 Linux内核I2C驱动 CHA5 usb-skeleton.c USB驱动框架 camera Spcaview相关代码 CHA6 skeleton ...

    嵌入式Linux驱动程序设计从入门到精通.part04.rar

    2 阻塞式读写 3 定时器 4 内存映射 5 /proc访问 6 工作队列 CHA3 LED.c LED灯驱动 button.c 键盘驱动 CHA4 Linux内核I2C驱动 CHA5 usb-skeleton.c USB驱动框架 camera Spcaview相关代码 CHA6 skeleton FrameBuffer...

    嵌入式Linux驱动程序设计从入门到精通.part06.rar

    2 阻塞式读写 3 定时器 4 内存映射 5 /proc访问 6 工作队列 CHA3 LED.c LED灯驱动 button.c 键盘驱动 CHA4 Linux内核I2C驱动 CHA5 usb-skeleton.c USB驱动框架 camera Spcaview相关代码 CHA6 skeleton FrameBuffer...

    嵌入式Linux驱动程序设计从入门到精通.part08.rar

    2 阻塞式读写 3 定时器 4 内存映射 5 /proc访问 6 工作队列 CHA3 LED.c LED灯驱动 button.c 键盘驱动 CHA4 Linux内核I2C驱动 CHA5 usb-skeleton.c USB驱动框架 camera Spcaview相关代码 CHA6 skeleton FrameBuffer...

    嵌入式Linux驱动程序设计从入门到精通.part07.rar

    2 阻塞式读写 3 定时器 4 内存映射 5 /proc访问 6 工作队列 CHA3 LED.c LED灯驱动 button.c 键盘驱动 CHA4 Linux内核I2C驱动 CHA5 usb-skeleton.c USB驱动框架 camera Spcaview相关代码 CHA6 skeleton FrameBuffer...

    嵌入式Linux驱动程序设计从入门到精通.part09.rar

    2 阻塞式读写 3 定时器 4 内存映射 5 /proc访问 6 工作队列 CHA3 LED.c LED灯驱动 button.c 键盘驱动 CHA4 Linux内核I2C驱动 CHA5 usb-skeleton.c USB驱动框架 camera Spcaview相关代码 CHA6 skeleton FrameBuffer...

    嵌入式Linux驱动程序设计从入门到精通.part05.rar

    2 阻塞式读写 3 定时器 4 内存映射 5 /proc访问 6 工作队列 CHA3 LED.c LED灯驱动 button.c 键盘驱动 CHA4 Linux内核I2C驱动 CHA5 usb-skeleton.c USB驱动框架 camera Spcaview相关代码 CHA6 skeleton FrameBuffer...

    嵌入式Linux驱动程序设计从入门到精通.part03.rar

    2 阻塞式读写 3 定时器 4 内存映射 5 /proc访问 6 工作队列 CHA3 LED.c LED灯驱动 button.c 键盘驱动 CHA4 Linux内核I2C驱动 CHA5 usb-skeleton.c USB驱动框架 camera Spcaview相关代码 CHA6 skeleton ...

    linux usb gadget从机设备驱动示例程序

    在linux gadget zero驱动的基础上实现了文件接口,阻塞读写,设备打开数量限制等功能,支持直接通过cat和echo的重定向机制操作此驱动,实现usb gadget驱动的读写,在linux3,3通过测试。 使用方法,将这些文件拷贝到...

    字符设备驱动程序的开发

    一个驱动程序应该检查 O_NONBLOCK 标志,以查看是否有非阻塞操作的请求。其它的标志用得比较少。需要注意的是,检查 read/write 权限应该是通过检查 f_mode 得到而不是 f_flags 。所有的标志定义在头文件 linux/...

    Embedfire-imx6#embed_linux_tutorial_ppt#POLL机制基本概念1

    POLL机制基本概念应用层poll()函数异步阻塞型IO同步阻塞:阻塞在一个文件的读写操作上(read\write),自己设备驱动唤醒自己异步阻塞:阻塞在多个文

    Linux2.6内核标准教程(共计8-- 第1个)

    《Linux2.6内核标准教程》适合Linux内核爱好者、Linux驱动开发人员、Linux系统工程师参考使用,也可以作为计算机及相关专业学生深入学 习操作系统的参考书。 引用: 目录 第1章 Linux内核学习基础 1 1.1 为什么...

    Linux2.6内核标准教程(共计8--第6个)

    《Linux2.6内核标准教程》适合Linux内核爱好者、Linux驱动开发人员、Linux系统工程师参考使用,也可以作为计算机及相关专业学生深入学 习操作系统的参考书。 引用: 目录 第1章 Linux内核学习基础 1 1.1 为什么...

    Linux2.6内核标准教程(共计8--第8个)

    《Linux2.6内核标准教程》适合Linux内核爱好者、Linux驱动开发人员、Linux系统工程师参考使用,也可以作为计算机及相关专业学生深入学 习操作系统的参考书。 引用: 目录 第1章 Linux内核学习基础 1 1.1 为什么...

    Linux2.6内核标准教程(共计8--第3个)

    《Linux2.6内核标准教程》适合Linux内核爱好者、Linux驱动开发人员、Linux系统工程师参考使用,也可以作为计算机及相关专业学生深入学 习操作系统的参考书。 引用: 目录 第1章 Linux内核学习基础 1 1.1 为什么...

    Linux2.6内核标准教程(共计8--第7个)

    《Linux2.6内核标准教程》适合Linux内核爱好者、Linux驱动开发人员、Linux系统工程师参考使用,也可以作为计算机及相关专业学生深入学 习操作系统的参考书。 引用: 目录 第1章 Linux内核学习基础 1 1.1 为什么...

    Linux2.6内核标准教程(共计8--第4个)

    《Linux2.6内核标准教程》适合Linux内核爱好者、Linux驱动开发人员、Linux系统工程师参考使用,也可以作为计算机及相关专业学生深入学 习操作系统的参考书。 引用: 目录 第1章 Linux内核学习基础 1 1.1 为什么...

    Linux2.6内核标准教程(共计8--第2个)

    《Linux2.6内核标准教程》适合Linux内核爱好者、Linux驱动开发人员、Linux系统工程师参考使用,也可以作为计算机及相关专业学生深入学 习操作系统的参考书。 引用: 目录 第1章 Linux内核学习基础 1 1.1 为什么...

Global site tag (gtag.js) - Google Analytics