版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ForFuture_/article/details/79407300
驱动设备申请及源码实现设备文件创建一体函数(miscdevice)
miscdevice是字符设备驱动的简化版本,方便实现一个简单的字符设备驱动。
只适用于没有同类型的设备驱动,也就是一个驱动只对应一个硬件。
相关变量及函数:
#include <linux/miscdevice.h>
struct miscdevice {
int minor; //指定次设备号,次设备号为255,则会自分配空闲的次设备号
//主设备号已固定为10
//次设备号在(0~255)之间
const char *name; //名字,指定的名字也是设备文件的名字,设备文件会在注册时自动创建
const struct file_operations *fops; //文件操作对象的地址
...
};
extern int misc_register(struct miscdevice * misc); //注册miscdevice对象
extern int misc_deregister(struct miscdevice *misc);//反注册
当我们实现miscdevice设备驱动时,初始化一个miscdevice对象,调用misc_register(对象的地址)来注册。
miscdevice的实现原理:
在内核源码drivers/char/misc.c文件里:
static int __init misc_init(void)
{
int err;
...
misc_class = class_create(THIS_MODULE, "misc"); //创建/sys/class/misc子目录,稍后创建设备信息用
...
if (register_chrdev(MISC_MAJOR, "misc", &misc_fops)) //当主设备号为10,次设备号(0~255)的所有设备文件打开操作时,会调用misc_fops对象里的open函数
goto fail_printk;
...
}
subsys_initcall(misc_init); //会在内核子系统初始化自动调用misc_init函数
misc_list是在misc.c文件里的一个全局内核链表头,用于通过miscdevice对象的list成员,存放所有的miscdevice对象。
int misc_register(struct miscdevice * misc)
{
struct miscdevice *c;
dev_t dev;
int err = 0;
INIT_LIST_HEAD(&misc->list);
mutex_lock(&misc_mtx);
//遍历链表,确认指定次设备号是否已用
list_for_each_entry(c, &misc_list, list) {
if (c->minor == misc->minor) {
mutex_unlock(&misc_mtx);
return -EBUSY;
}
}
if (misc->minor == MISC_DYNAMIC_MINOR) {
//当需要动态分配空闲的次设备号时的操作
...
}
//创建设备文件
dev = MKDEV(MISC_MAJOR, misc->minor);
misc->this_device = device_create(misc_class, misc->parent, dev,
misc, "%s", misc->name);
list_add(&misc->list, &misc_list); //通过list成员,把miscdevice对象加入misc_list链表
//也可以在misc_list链表里根据次设备号找到相应的miscdevice对象
out:
mutex_unlock(&misc_mtx);
return err;
}
static const struct file_operations misc_fops = {
.open = misc_open,
...
};
//当主设备号为10,次设备号(0~255)的所有设备文件打开操作时,会调用misc_fops对象里的open函数
static int misc_open(struct inode * inode, struct file * file)
{
int minor = iminor(inode); //获取打开的设备文件的次设备号
struct miscdevice *c;
int err = -ENODEV;
const struct file_operations *old_fops, *new_fops = NULL;
mutex_lock(&misc_mtx);
//遍历misc_list链表,根据次设备号minor找到对应的miscdevice对象
list_for_each_entry(c, &misc_list, list) {
if (c->minor == minor) {
new_fops = fops_get(c->fops); //找到后,则用new_fops指定存放miscdevice对象的fops成员存放file_operations对象的地址,也就是我们自己miscdevice设备驱动里实现file_operatins对象的地址
break;
}
}
err = 0;
old_fops = file->f_op;
file->f_op = new_fops; //把file对象的f_op成员指向我们的file_operations对象的地址,以后我们的file_operations里实现的函数就会得到调用
//如果我们实现的file_operations对象里有open函数,则调用
if (file->f_op->open) {
file->private_data = c;
err=file->f_op->open(inode,file);
if (err) {
fops_put(file->f_op);
file->f_op = fops_get(old_fops);
}
}
fops_put(old_fops);
fail:
mutex_unlock(&misc_mtx);
return err;
}
测试代码(test.c):
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
ssize_t myread(struct file *fl, char __user *buf, size_t len, loff_t *off)
{
printk("in myread ...\n");
return 0;
}
struct file_operations fops = {
.read = myread,
};
struct miscdevice mymdev = {
.minor = MISC_DYNAMIC_MINOR, //255
.name = "mymdev",
.fops = &fops,
};
static int __init test_init(void)
{
return misc_register(&mymdev);
}
static void __exit test_exit(void)
{
misc_deregister(&mymdev);
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");