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

 这个代码并不能实现真正的串口数据收发,但其能够接收用户想要发送的数据,并且将该数据原封不动回环给收端。
 字符设备驱动除了前面搭好的框架后,最终要实现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\n");
	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");
posted @ 2020-04-21 11:38  hansenn  阅读(225)  评论(0编辑  收藏  举报