字符设备驱动(中)
- 在上一篇博客里,讨论了字符设备注册以及字符设备驱动的相关框架,这里我们进一步完善之前的字符设备驱动的程序。上篇文章链接如下:
- 字符设备驱动(上)
一、一个驱动支持多个设备
- 我们平时写设备驱动程序的时候,肯定遇到过这种情况,一类设备有多个个体(比如系统上有两个串口)。此时,我们可能会想到多一个设备我在多写一个同样的驱动程序就好啦,或者说同一中驱动程序写两份呢?这样当然可以啦,但是呢,这样并不是最好的解决办法。如果我们一个设备上面的设备有10个个体呢,而我们的驱动程序是要编译进内核的而内核又是对内存有着比较高的要求的,同一类设备你编译进去那么多的代码自然很浪费内存又不够简洁。当然了,我们可以采用一个讨巧的办法来处理这一类问题了。那就是一个驱动程序支持多个设备。
- 这个我们应该怎么做呢?首先我们应该向内核注册多个设备号,其次就是添加cdev对象(这里为什么不说结构体呢,因为在驱动编程中我们用到了面向对象的编程方法,即把每一个设备当做一个对象来处理,为其添加属性和方法)时需要指明该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
#define VSER_DEV_NAME "vser"
static struct cdev vsdev;
static DEFINE_KFIFO(vsfifo0,char,32);
static DEFINE_FIFO(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;
break;
case 1:
filp->private_data = &vsfifo1;
break;
}
return 0;
}
static int vser_release(struct inode *inode,struct file *filp)
{
return 0;
}
static ssize_t vser_read(struct file *filp,char __vser *buf,size_t count,loff_t *ops)
{
unsigned int copied = 0;
struct kfifo *vsfifo = filp->private_data;
kfifo_to_user(vsfifo,buf,count,&copied);
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;
}
static struct file_opeations vser_ops = {
.owner = THIS_MODULE,
.open = vser_open,
.release = vser_read,
.write = vser_write,
};
static int __init vser_init(void)
{
int ret;
dev_t dev;
dev = MKDEV(VSER_MAJOR,VSER_MINOR);
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;
}
return 0;
add_err:
unregister_chrdev_region(dev,VSER_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,VSER_DEV_CNT);
}
module_init(vser_init);
module_exit(vser_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A simple character device driver");
MODULE_ALIAS("virtual-serial");
二、小结
- 到目前为止呢,我们的字符设备的驱动程序已经近乎完成,当然了,其实并不是很完善。比如我们写了一个串口设备的驱动,我们又如何去控制一个串口设备的波特率呢?等等。当然了,并不是无法解决的,只是我们的知识水平还不够。为了解决此问题呢,Linux专门开发出了ioctl函数用于做上面提到的问题。那么ioctl是什么呢?请见下一小节。