Linux字符设备驱动 - VF(Virtual File) - ver 2

来源:互联网 发布:新疆2016年6月4g网络 编辑:程序博客网 时间:2024/05/16 05:23
写一些Linux简单字符设备驱动, 并陆续添加一些功能, 理解与实践相关内核机制.

VF, Virtual File, 驱动程序在内核空间中申请一片区并创建设备文件, 用户空间的应用程序读写该设备, 效果同读写普通文件一样.


ver 1, 最简单的版本
ver 2, 添加了信号量

每个打开设备的用户程序共享文件指针, 所以, 以下测试程序将输出aaccbb

终端1执行a测试程序:

#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>int main(){int fd_in;int fd_ou = open("/dev/vf", O_RDWR);char *x = "aa";write(fd_ou, x, 2);sleep(6);char *y = "bb";write(fd_ou, y, 2);lseek(fd_ou, 0, SEEK_SET);char t[12] = { 0 };read(fd_ou, t, 10);printf("%s\n", t);}

终端2在a执行后立刻执行b测试程序:

#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>int main(){int fd_in;int fd_ou = open("/dev/vf", O_RDWR);char *z = "cc";write(fd_ou, z, 2);}

vf2.c

#include <linux/module.h>#include <linux/types.h>#include <linux/fs.h>#include <linux/errno.h>#include <linux/mm.h>#include <linux/sched.h>#include <linux/init.h>#include <linux/cdev.h>#include <linux/device.h>#include <linux/slab.h>#include <asm/uaccess.h>int vf_open (struct inode *, struct file *);int vf_release (struct inode *, struct file *);ssize_t vf_read (struct file *, char __user *, size_t, loff_t *);ssize_t vf_write (struct file *, const char __user *, size_t, loff_t *);loff_t vf_llseek (struct file *, loff_t, int);#define DEVICE_NAME"vf"#define DATA_SIZE_INIT0x1000#define DATA_SIZE_MAX0x10000struct vf_dev{unsigned long size;unsigned long pos;char *data;struct semaphore sem;struct cdev cdev;};static dev_tvf_dev_num;static struct vf_dev*vf_devp;static struct file_operationsvf_fops = {.owner= THIS_MODULE,.open= vf_open,.release= vf_release,.read= vf_read,.write= vf_write,.llseek = vf_llseek,};static struct class*vf_class;static struct device*vf_device;int __init vf_init(void){int ret;ret = alloc_chrdev_region(&vf_dev_num, 0, 1, DEVICE_NAME);if (ret < 0){printk(KERN_ERR "alloc_chrdev_region() failed for %s\n", DEVICE_NAME);goto out_err_0;}vf_devp = kmalloc(sizeof(struct vf_dev), GFP_KERNEL);if (vf_devp == NULL){printk(KERN_ERR "kmalloc() failed for %s\n", DEVICE_NAME);goto out_err_0;}memset(vf_devp, 0, sizeof(struct vf_dev));vf_devp->size = DATA_SIZE_INIT;vf_devp->data = kmalloc(DATA_SIZE_INIT, GFP_KERNEL);if (vf_devp->data == NULL){printk(KERN_ERR "kmalloc() failed for %s\n", DEVICE_NAME);goto out_err_0;}memset(vf_devp->data, 0, DATA_SIZE_INIT);sema_init(&vf_devp->sem, 1);cdev_init(&vf_devp->cdev, &vf_fops);vf_devp->cdev.owner = THIS_MODULE;ret = cdev_add(&vf_devp->cdev, vf_dev_num, 1);if (ret){printk(KERN_ERR "cdev_add() failed for %s\n", DEVICE_NAME);goto out_err_1;}vf_class = class_create(THIS_MODULE, DEVICE_NAME);if(IS_ERR(vf_class)){ret = PTR_ERR(vf_class);printk(KERN_ERR "class_create() failed for %s\n", DEVICE_NAME);goto out_err_2;}vf_device = device_create(vf_class, NULL, vf_dev_num, NULL, DEVICE_NAME);if (IS_ERR(vf_device)){printk(KERN_ERR "device_create() failed for %s\n", DEVICE_NAME);goto out_err_3;}return 0;out_err_3:device_destroy(vf_class, vf_dev_num);out_err_2:class_destroy(vf_class);out_err_1:cdev_del(&vf_devp->cdev);out_err_0:unregister_chrdev_region(vf_dev_num, 1);if (vf_devp->data)kfree(vf_devp->data);if (vf_devp)kfree(vf_devp);return ret;}void __exit vf_exit(void){device_destroy(vf_class, vf_dev_num);class_destroy(vf_class);cdev_del(&vf_devp->cdev);unregister_chrdev_region(vf_dev_num, 1);if (vf_devp->data)kfree(vf_devp->data);if (vf_devp)kfree(vf_devp);}int vf_open (struct inode *inode, struct file *file){struct vf_dev *dev;dev = container_of(inode->i_cdev, struct vf_dev, cdev);file->private_data = dev;return 0;/* success */}int vf_release (struct inode *inode, struct file *file){return 0;}ssize_t vf_read (struct file *file, char __user *buf, size_t len, loff_t *ppos){int ret;struct vf_dev *dev;dev = file->private_data;if (down_interruptible(&dev->sem))return -ERESTARTSYS;if (dev->pos + len >= dev->size)len = dev->size - dev->pos - 1;if (copy_to_user(buf, dev->data + dev->pos, len)){ret = -EFAULT;goto out;}dev->pos += len;ret = len;out:up(&dev->sem);return ret;}ssize_t vf_write (struct file *file, const char __user *buf, size_t len, loff_t *ppos){int ret;struct vf_dev *dev;dev = file->private_data;if (down_interruptible(&dev->sem))return -ERESTARTSYS;if (dev->pos + len >= dev->size)len = dev->size - dev->pos - 1;if (copy_from_user(dev->data + dev->pos, buf, len)){ret = -EFAULT;goto out;}dev->pos += len;ret = len;out:up(&dev->sem);return ret;}loff_t vf_llseek (struct file *file, loff_t offset, int fromwhere){struct vf_dev *dev;dev = file->private_data;switch (fromwhere){case SEEK_SET:dev->pos = offset;break;case SEEK_CUR:dev->pos = file->f_pos + offset;break;case SEEK_END:dev->pos = dev->size + offset;break;}return 0;}MODULE_AUTHOR("SLF");MODULE_LICENSE("GPL");module_init(vf_init);module_exit(vf_exit);