嵌入式Linux驱动初探 虚拟串口设备驱动编写

1.说明

所谓虚拟串口设备意为这个串口是虚拟的,不能用来实现与下位机的串口收发。但是他可以将从用户那儿收到的数据,原封不动的回传给用户。相当于一个回环。
这一功能的实现主要是在驱动中实现一个FIFO。驱动接收到用户数据后,先将之放入FIFO,当用户需要数据(读取数据)时,驱动从FIFO中把数据读出,回传给用户。

2 . FIFO相关函数

FIFO在内核空间已经实现好了,只需要调用对应的宏 和函数即可:

DEFINE_KFIFO(fifo, type, size)
kfifo_from_user(fifo, from, len, copied)
kfifo_to_user(fifo, to, len, copied)

DEFINE_KFIFO是用于初始化一个FIFO,名字叫fifo,FIFO里的数据类型为type,FIFO大小为size。
kfifo_from_user用于从用户(应用层)获取数据并放入FIFO,fifo为初始化好的FIFO,from为数据,len为数据长度,copied用于返回实际拷贝进FIFO的数据的长度。
kfifo_to_user用于从从FIFO中取出数据,to为返回的数据指针,fifo,len和copied与上面类似。

3.贴代码

代码有详细注释。

//包含必要的头文件
#include <linux/init.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 DEV_CNT 1
#define	VSER_DEV_NAME "vser"

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Mr.Zhong <[email protected]>");
MODULE_DESCRIPTION("A simple Module");
MODULE_ALIAS("Visualport");
/*init a FIFO*/
DEFINE_KFIFO(vsfifo,char,32);
/*declare a device*/
static struct cdev vsdev;
//下面两个接口在这里不是必须的,所以没必要实现,直接返回即可。
static int vser_open(struct inode* inode,struct file* filp)
{
	return 0;
}

static int vser_relase(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;
	kfifo_to_user(&vsfifo,buf,count,&copied);//将FIFO里的数据传送到用户空间

	return copied;
}
//从用户空间传数据到内核空间的FIFO,从用户角度就是write
static ssize_t vser_write(struct file* filp,const char __user *buf,size_t count,loff_t *pos)
{
	unsigned int copied = 0;
	/*from user space copy data to kernel space*/
	kfifo_from_user(&vsfifo,buf,count,&copied);

	return copied;
}
/*relize some file operations */
//这里是将实现的接口和内核关联起来,以后在应用层使用open read write 函数就会对应执行内核空间的//vser_open、read 、write 。
static struct file_operations vser_ops = {
	.owner = THIS_MODULE,
	.open = vser_open,
	.release = vser_relase,
	.read = vser_read,
	.write = vser_write,
	};

/*register device*/
static int __init vser_init(void)
{
	int ret;
	dev_t dev;
	dev = MKDEV(VSER_MAJOR,VSER_MINOR); //由主从设备号得到最终设备号
	ret = register_chrdev_region(dev,DEV_CNT,VSER_DEV_NAME); //注册设备
	if(ret)
		 goto reg_err; //在内核编程时习惯用跳转指令到统一的地方去处理错误。
	cdev_init(&vsdev,&vser_ops); //初始化设备,也就是将上面操作文件的函数实现和设备关联起来
	vsdev.owner = THIS_MODULE;//相当于c++中的this指针,以后就可以通过这个指针访问到特定的设备了

	ret = cdev_add(&vsdev,dev,DEV_CNT);//添加设备
	if(ret)
		goto add_err;
	printk(KERN_EMERG "init KFIFO driver\n");
	return 0;

add_err:
	unregister_chrdev_region(dev,DEV_CNT);//出错,卸载设备
	
reg_err:
	return ret;
}

static void __exit vser_exit(void)
{
	dev_t dev;
	dev = MKDEV(VSER_MAJOR,VSER_MINOR);
	
	cdev_del(&vsdev);
	unregister_chrdev_region(dev,DEV_CNT);卸载设备

	printk(KERN_EMERG "exit KFIFO \n");
}
/*alias*/
module_init(vser_init); //这里是感受内核这是模块初始化的入口
module_exit(vser_exit);

4.测试

将编写好的代码编译得到驱动模块(拓展名*.ko)。
然后加载模块到内核:

sudo insmod virtualport.ko

然后查看设备是否加载:

cat /proc/devices | grep vser

可以看到设备已经加载到内核,主设备号为256。
在这里插入图片描述

然后在/dev目录下新建节点,并发送数据,操作流程如下:

$ mknod /dev/vser0 c 256 0
$ echo "hello driver" > vser0
$ cat vser0
hello drives

从上面返回的数据可知,驱动实现了用户向FIFO发送数据,并且在用户需要的时候将数据原封不动的返回给用户。

发布了18 篇原创文章 · 获赞 92 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_27350133/article/details/84501595
今日推荐