编写了一个虚拟的驱动,实现的功能是在读设备时阻塞,直到有数据写入设备,然后才能读出写入的数据。
其中有信号量的操作与阻塞非阻塞IO的操作,
最后写了一个应用程序进行验证
驱动如下:
#include
#include
#include
#include
#include
#include
#include
#include
/*for spinlock and semaphore*/
#include
#include
#include
/*for task management*/
#include
#include
#include
#define DEVICE_NAME "pcio"
#define DRIVER_NAME "mypcio"//加载驱动之后会在/dev/目录下发现mypcio,应用程序可以使用
#define PCIO_MAJOR 0 //预设的pcio的主设备号
static int pcio_major = PCIO_MAJOR;
static int pcioOpenCount=0;
static int flag=0;
static int global_var = 0;
struct class *pcio_class;
static struct device *pcioDevice=NULL;
static wait_queue_head_t wqueue;
struct pcio_dev
{
struct cdev cdev;//cdev结构体
//信号量
struct semaphore sem;
//等待队列头
//struct wait_queue_head_t* wqueue;
//unsigned char mem[MYKEY_SIZE];//全局内存
};
struct pcio_dev *pcio_devp;//设备结构体指针
//打开函数
static int pcio_open(struct inode *inode, struct file *file)
{
pcioOpenCount++;
return 0;
}
//关闭函数
static int pcio_close(struct inode *inode, struct file *file)
{
pcioOpenCount--;
return 0;
}
//读函数
static ssize_t pcio_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
//等待事件
if(wait_event_interruptible(wqueue,flag!=0))
{
return -ERESTARTSYS;
}
//获取信号量
if(down_interruptible(&pcio_devp->sem))
{
return -ERESTARTSYS;
}
//从内核空间到用户空间的数据拷贝
if(copy_to_user(buff,&global_var,sizeof(int)) != 0)
{
/*release semaphore*/
up(&pcio_devp->sem);
return -EFAULT;
}
/*data unaccessible flag*/
flag = 0;
/*release semaphore*/
up(&pcio_devp->sem);
return sizeof(int);
}
//写函数
static ssize_t pcio_write(struct file *filp, const char __user *buff, size_t count, loff_t *offp)
{
/*get semaphore*/
if(down_interruptible(&pcio_devp->sem))
{
return -ERESTARTSYS;
}
printk("down_interruptible ok!\n");
if(copy_from_user(&global_var,buff,sizeof(int) != 0))
{
/*release semaphore*/
up(&pcio_devp->sem);
return -EFAULT;
}
/*release semaphore*/
up(&pcio_devp->sem);
/*data ready*/
flag = 1;
/*wake up the waiting task*/
wake_up_interruptible(&wqueue);
return sizeof(int);
}
//dev_fops操作指令集
static struct file_operations pcio_fops =
{
.owner =THIS_MODULE,
.open = pcio_open,
.release = pcio_close,
.write = pcio_write,
.read = pcio_read,
//.poll = pcio_poll,
//.unlocked_ioctl = my2416_keys_ioctl,//这里必须是unlocked_ioctl而不是ioctl。
};
/*//第三步:混杂设备定义
static struct miscdevice my2416Ledmisc =
{
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,//加载驱动之后会在/dev/目录下发现mykeys,应用程序可以使用
.fops = &pcio_fops,
};
*/
static void Pcio_setup_cdev(struct pcio_dev *dev,int index)
{
int err, devno = MKDEV(pcio_major,index);
/*初始化cdev,并将相关的文件操作添加进来*/
cdev_init(&dev->cdev, &pcio_fops);
dev->cdev.owner = THIS_MODULE;
//dev->cdev.ops = &pcio_fops;
/*注册字符设备*/
err = cdev_add(&dev->cdev, devno, 1);
if (err)
printk("Error %d\n", err);
else
printk("have finish add\n");
}
/*驱动模块加载*/
static int pcio_dev_init(void)
{
int ret;
dev_t devno;
printk("DEV:register pcio\n");
/*创建一个设备号*/
devno=MKDEV(pcio_major,0);
/*注册一个设备号*/
/*如果定义了主设备号采用静态申请的方式*/
if(pcio_major)
{
ret=register_chrdev_region(devno,1,DEVICE_NAME);
}
else//动态申请设备号
{
ret= alloc_chrdev_region(&devno,0,1,DEVICE_NAME);
pcio_major=MAJOR(devno);
}
if(ret<0)
{
printk (DEVICE_NAME " DEV:can't register\n");
return ret;
}
//动态申请设备结构体内存
pcio_devp=kmalloc(sizeof(struct pcio_dev), GFP_KERNEL);
if(!pcio_devp)//申请失败
{
printk("DEV:kmalloc faile\n");
ret=-ENOMEM;
goto fail_malloc;
}
printk("DEV:kmalloc succeed\n");
/*清除空间*/
memset(pcio_devp,0,sizeof(struct pcio_dev));
/*创建一个设备*/
Pcio_setup_cdev(pcio_devp,0);
//class_create和device_create函数是为了自动在/dev下创建DRIVER_NAME设备文件。
//创建一个类,这个类存放于sysfs下面
pcio_class=class_create(THIS_MODULE,DRIVER_NAME);
if(IS_ERR(pcio_class))
{
ret = PTR_ERR(pcio_class);
printk("DEV:class create faikey\n");
goto class_create_fail;
}
//在/dev目录下创建相应的设备节点
pcioDevice = device_create(pcio_class,NULL,devno,NULL,DRIVER_NAME);
if(IS_ERR(pcioDevice))
{
ret = PTR_ERR(pcioDevice);
printk("DEV:device_create faile\n");
goto device_create_faile;
}
//初始化信号量
sema_init(&pcio_devp->sem,1);
//初始化等待队列
init_waitqueue_head(&wqueue);
fail_malloc:
unregister_chrdev_region(devno,1);//释放设备号
class_create_fail:
unregister_chrdev_region(MKDEV(pcio_major, 0), 1);//释放设备号
device_create_faile:
class_destroy(pcio_class);/*注销创建的设备类*/
return ret;
}
static void __exit pcio_dev_exit(void)
{
/*注销设备*/
device_destroy(pcio_class,MKDEV(pcio_major, 0));
/*注销创建的设备类*/
class_destroy(pcio_class);
/*字符设备注销*/
cdev_del(&pcio_devp->cdev);//注销cdev
kfree(pcio_devp);/*释放设备结构体内存*/
unregister_chrdev_region(MKDEV(pcio_major, 0), 1);//释放设备号
printk(DEVICE_NAME " exit\n");
}
module_init(pcio_dev_init);
module_exit(pcio_dev_exit);
MODULE_AUTHOR("Zhao Yidong ");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("A sampleIO module");
MODULE_ALIAS("a sample module");
相应的makefile文件如下:
#Makefile for PCIOModule.c.c
#ARCH=arm
#CROSS_COMPILE=arm-linux-gnueabihf-
ifneq ($(KERNELRELEASE),)
obj-m := PCIOModule.o
else
#bbblack kernel
#KERNELDIR ?= /usr/local/ti-sdk-beagleboard/board-support/linux-3.3.7
#PC kernel
KERNELDIR ?=/lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
app: app.c
$(CROSS_COMPILE)gcc -o app app.c
clean:
$(MAKE) -C $(KERNELDIR) M=$(PWD) clean
endif
#include
#include
#include
#include
#include
#include
#include
/*
*/
int main(int argc, char** args) {
int fd, num;
if (argc >= 2) {
if (strcmp(args[1], "0") == 0) {
printf("mode read!\n");
/*opemn device*/
fd = open("/dev/mypcio", O_RDWR, S_IRUSR | S_IWUSR);
if (fd != -1) {
while (1) {
read(fd, &num, sizeof(int));//阻塞的读取,直到有数据才返回
printf("mypcio=%d\n",num);
if (num == 0) {
close(fd);
break;
}
}//while
} else {
printf("error:device open error!\n");
}
} else if (strcmp(args[1], "1") == 0) {
printf("mode write!\n");
/*opemn device*/
fd = open("/dev/mypcio", O_RDWR, S_IRUSR | S_IWUSR);
if (fd != -1) {
while (1) {
/*writing test*/
printf("print number to write: ");
scanf("%d", &num);
write(fd, &num, sizeof(int));
if (num == 0) {
close(fd);
break;
}
}
} else {
printf("error:device open error!\n");
}
}
}
return 0;
}