zoukankan      html  css  js  c++  java
  • 第三章(扩展)——虚拟串口设备

     这个代码并不能实现真正的串口数据收发,但其能够接收用户想要发送的数据,并且将该数据原封不动回环给收端。
     字符设备驱动除了前面搭好的框架后,最终要实现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");
    
  • 相关阅读:
    Compression algorithm (deflate)
    tcpip数据包编码解析(chunk and gzip)_space of Jialy_百度空间
    What's the difference between the "gzip" and "deflate" HTTP 1.1 encodings?
    gzip压缩算法: gzip 所使用压缩算法的基本原理
    Decompressing a GZip Stream with Zlib
    Frequently Asked Questions about zlib
    how to decompress gzip stream with zlib
    自己动手写web服务器四(web服务器是如何通过压缩数据,web服务器的gzip模块的实现)
    What's the difference between the "gzip" and "deflate" HTTP 1.1 encodings?
    C语言抓http gzip包并解压 失败 C/C++ ChinaUnix.net
  • 原文地址:https://www.cnblogs.com/hansenn/p/12743195.html
Copyright © 2011-2022 走看看