嵌入式linux:阻塞与非阻塞驱动

2019-07-13 01:02发布

编写了一个虚拟的驱动,实现的功能是在读设备时阻塞,直到有数据写入设备,然后才能读出写入的数据。 其中有信号量的操作与阻塞非阻塞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! "); 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 ", err); else printk("have finish add "); } /*驱动模块加载*/ static int pcio_dev_init(void) { int ret; dev_t devno; printk("DEV:register pcio "); /*创建一个设备号*/ 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 "); return ret; } //动态申请设备结构体内存 pcio_devp=kmalloc(sizeof(struct pcio_dev), GFP_KERNEL); if(!pcio_devp)//申请失败 { printk("DEV:kmalloc faile "); ret=-ENOMEM; goto fail_malloc; } printk("DEV:kmalloc succeed "); /*清除空间*/ 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 "); 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 "); 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 "); } 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! "); /*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 ",num); if (num == 0) { close(fd); break; } }//while } else { printf("error:device open error! "); } } else if (strcmp(args[1], "1") == 0) { printf("mode write! "); /*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! "); } } } return 0; }
最后打开两个终端,运行测试程序如下图,每次在写终端写入一个数,在读取程序终端就会看到写入的数据。