• 第三章(扩展)——虚拟串口设备


     这个代码并不能实现真正的串口数据收发,但其能够接收用户想要发送的数据,并且将该数据原封不动回环给收端。
     字符设备驱动除了前面搭好的框架后,最终要实现file_operations.

    #include <linux/init.h>
    #include <linux/kernel.h>
    #include <linux/module.h>
    #include <linux/fs.h>
    #include <linux/cdev.h>
    #include <linux/kfifo.h>
    
    #define VSER_MAJOR 256
    #define VSER_MINOR 0
    #define VSER_DEV_CNT 1
    #define VSER_DEV_NAME "vser"
    
    static struct cdev vsdev;
    DEFINE_KFIFO(vsfifo, char, 32); //定义并初始化FIFO对象
    
    static int vser_open(struct inode *inode, struct file *filp){
    	return 0;
    }
    
    static int vser_release(struct inode *inode, struct file *filp){
    	return 0;
    }
    /*
    char __user *buf:用户空间的内存起始地址, __user用来提示其来自用户空间
    size_t count:用户想要读写多少字节的数据
    loff_t *pos:文件位置指针(这里没用)
    */
    static ssize_t vser_read(struct file *filp, char __user *buf, size_t count, loff_t *pos) {
    	unsigned int copied = 0;
    	kfifo_to_user(&vsfifo, buf, count, &copied); //将FIFO数据取出,复制到用户空间(内核视角)
    	return copied;
    }
    
    static ssize_t vser_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos) {
    	unsigned int copied = 0;
    	kfifo_from_user(&vsfifo, buf, count, &copied);
    	return copied;
    }
    
    static struct file_operations vser_ops = { //定义了对字符设备的不同操作
    	.owner = THIS_MODULE;
    	.open = vser_open;
    	.release = vser_release;
    	.read = vser_read;
    	.write = vser_write;
    }
    
    //加载模块
    static int __init vser_init(void) {
    
        int ret;
    	dev_t dev;
    	dev = MKDEV(VSER_MAJOR, VSER_MINOR); //将主次设备号合并,主设备号占12位,次设备号占20位
    	ret = register_chrdev_region(dev, VSER_DEV_CNT, VSER_DEV_NAME);
    	if(ret)
    		goto reg_err;
    
    	cdev_init(&vsdev, &vser_ops);
    	vsdev.owner = THIS_MODULE;
    	
    	ret = cdev_add(&vsdev, dev, VSER_DEV_CNT);
    	if(ret)
    		goto add_err;
    	
    add_err:
        unregister_chrdev_region(dev, VSER_DEV_CNT);
    	
    reg_err:
        return ret;
    }
    
    //卸载模块
    static void __exit vser_exit(void) {
        printk("vser_exit
    ");
    	dev_t dev;
    	dev = MKDEV(VSER_MAJOR, VSER_MINOR);
    	cdev_del(&vsdev);
    	unregister_chrdev_region(dev, VSER_DEV_CNT);
    }
    
    module_init(vser_init);
    module_exit(vser_exit);
    MODULE_LICENSE("GPL"); //合法协议
    

     按照下面的步骤可进行验证:

    # mknod /dev/vser0 c 256 0
    # make
    # make modules_install
    # depmod
    # modprobe vser
    # echo "vser driver test" > /dev/vser0
    # cat /dev/vser0
    

    一个驱动支持多个设备

    多个设备引入的变化是什么?
    首先我们应该向内核注册多个设备号,其次在添加cdev对象时明确该对象管理了多个设备或者添加多个cdev设备,每个cdev管理一个设备。

    ...
    #define VSER_DEV_CNT 2 //2个设备
    #define VSER_DEV_NAME "vser"
    
    static struct cdev vsdev;
    DEFINE_KFIFO(vsfifo0, char, 32); //定义并初始化FIFO对象
    DEFINE_KFIFO(vsfifo1, char, 32);
    
    static int vser_open(struct inode *inode, struct file *filp){
    	switch (MINOR(inode->i_rdev)){
    		default:
    		case 0:
    		filp->private_data = &vsfifo0; //private_data成员是一个void *类型的指针,是驱动私有的。
    		break;
    		case 1:
    		filp->private_data = &vsfifo1;
    		break;
    	}
    }
    
    static ssize_t vser_read(struct file *filp, char __user *buf, size_t count, loff_t *pos) {
    	unsigned int copied = 0;
    	struct kfifo *vsfifo = filp->private_data;
    	kfifo_to_user(&vsfifo, buf, count, &copied); //将FIFO数据取出,复制到用户空间(内核视角)
    	return copied;
    }
    
    static ssize_t vser_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos) {
    	unsigned int copied = 0;
    	struct kfifo *vsfifo = filp->private_data;
    	kfifo_from_user(&vsfifo, buf, count, &copied);
    	return copied;
    }
    ...
    

     还可以将每一个cdev对象对应到一个设备实现支持:

    #include <linux/init.h>
    #include <linux/kernel.h>
    #include <linux/module.h>
    #include <linux/fs.h>
    #include <linux/cdev.h>
    #include <linux/kfifo.h>
    
    #define VSER_MAJOR 256
    #define VSER_MINOR 0
    #define VSER_DEV_CNT 2 //2个设备
    #define VSER_DEV_NAME "vser"
    
    static struct cdev vsdev;
    DEFINE_KFIFO(vsfifo0, char, 32); //定义并初始化FIFO对象
    DEFINE_KFIFO(vsfifo1, char, 32); 
    
    struct vser_dev {
    	struct kfifo *fifo;
    	struct cdev cdev;
    }
    
    static struct vser_dev vsdev[2]
    
    static int vser_open(struct inode *inode, struct file *filp){
    	filp->private_data = container_of(inode->i_cdev, struct vser_dev, cdev); 
    	//根据结构成员的地址反向得到结构的起始地址
    	return 0;
    }
    
    static int vser_release(struct inode *inode, struct file *filp){
    	return 0;
    }
    
    static ssize_t vser_read(struct file *filp, char __user *buf, size_t count, loff_t *pos) {
    	unsigned int copied = 0;
    	struct vser_dev *dev = filp->private_data;
    	kfifo_to_user(&dev->fifo, buf, count, &copied); //将FIFO数据取出,复制到用户空间(内核视角)
    	return copied;
    }
    
    static ssize_t vser_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos) {
    	unsigned int copied = 0;
    	struct vser_dev *dev = filp->private_data;
    	kfifo_from_user(&dev->fifo, buf, count, &copied);
    	return copied;
    }
    
    static struct file_operations vser_ops = { //定义了对字符设备的不同操作
    	.owner = THIS_MODULE;
    	.open = vser_open;
    	.release = vser_release;
    	.read = vser_read;
    	.write = vser_write;
    }
    
    //加载模块
    static int __init vser_init(void) {
    	
    	int i;
        int ret;
    	dev_t dev;
    	dev = MKDEV(VSER_MAJOR, VSER_MINOR); //将主次设备号合并,主设备号占12位,次设备号占20位
    	ret = register_chrdev_region(dev, VSER_DEV_CNT, VSER_DEV_NAME);
    	if(ret)
    		goto reg_err;
    	
    	for(i=0;i<VSER_DEV_CNT;i++) {
    		cdev_init(&vsdev[i].cdev, &vser_ops);
    		vsdev[i].cdev.owner = THIS_MODULE;
    		vsdev[i].fifo = i == 0? (struct kfifo *)&vsfifo0 : (struct kfifo *)&vsfifo1;
    		
    		ret = cdev_add(&vsdev[i].cdev, dev+i, 1);
    		if(ret)
    			goto add_err;
    	}
    	
    add_err:
        for(--i;i>0;--i) {
    		cdev_del(&vsdev[i].cdev);
    	}
        unregister_chrdev_region(dev, VSER_DEV_CNT);
    	
    reg_err:
        return ret;
    }
    
    //卸载模块
    static void __exit vser_exit(void) {
    	int i;
    	dev_t dev;
    	dev = MKDEV(VSER_MAJOR, VSER_MINOR);
    	for(i=0;i<VSER_DEV_CNT;i++) {
    		cdev_del(&vsdev[i].cdev);
    	}
    	unregister_chrdev_region(dev, VSER_DEV_CNT);
    }
    
    module_init(vser_init);
    module_exit(vser_exit);
    MODULE_LICENSE("GPL");
    
  • 相关阅读:
    几款免费的支持HTML5的音频视频转换软件推荐
    2 宽度优先爬虫和带偏好的爬虫(4)
    Hadoop源代码分析(三)
    Hadoop源代码分析(四)
    C# 收邮件
    关于Adobe flash palyer 安装出现的问题解决方案
    C#调用java类、jar包方法。
    EF 4.3 的一些基础使用
    .net数据库连接池问题:在同一页面使用一段时间后,提示超时,连接池不够用这类的提示!
    使用Google CDN的JSAPI服务来提供加载各类JS库的方法
  • 原文地址:https://www.cnblogs.com/hansenn/p/12743195.html
Copyright © 2020-2023  润新知