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