1.miscdevice定义
在Linux驱动中把无法归类的设备定义为混杂设备(用miscdevice结构体表述)。miscdevice共享一个主设备号MISC_MAJOR(即10),但次设备号不同。 所有的miscdevice设备形成了一个链表,对设备访问时内核根据次设备号查找对应的miscdevice设备,然后调用其file_operations结构中注册的文件操作接口进行操作。
2.miscdevice、file_operations结构
miscdevice结构定义如下:
struct miscdevice {
int minor;
const char *name;
const struct file_operations *fops;
struct list_head list;
struct device *parent;
struct device *this_device;
const struct attribute_group **groups;
const char *nodename;
umode_t mode;
};
minor表示次设备号,若由系统自动配置,则可以设置为MISC_DYNANIC_MINOR
name表示设备名
fops表示文件操作集
file_operations结构定义如下:
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
int (*iterate) (struct file *, struct dir_context *);
int (*iterate_shared) (struct file *, struct dir_context *);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **, void **);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
unsigned (*mmap_capabilities)(struct file *);
#endif
ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
loff_t, size_t, unsigned int);
int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t,
u64);
ssize_t (*dedupe_file_range)(struct file *, u64, u64, struct file *,
u64);
} __randomize_layout;
通常使用到的结构成员如下:
owner //拥有该结构的模块的指针,一般为THIS_MODULES
unlocked_ioctl //执行设备I/O控制命令
llseek //用来修改文件当前的读写位置
open //打开文件
read //读数据
write //写数据
3.创建miscdevice
利用学习的开源LightNVM子系统中创建的lightnvm杂项设备来描述创建过程。
int nonseekable_open(struct inode *inode, struct file *filp); //由不需要可搜索文件描述符的子系统使用 long nvm_ctl_ioctl(struct file *file, uint cmd, unsigned long arg); loff_t noop_llseek(struct file *file, loff_t offset, int whence); //实现当用户空间期望搜索成功但(设备)文件实际上无法执行搜索的情况
首先定义miscdevice和file_operations
struct file_operations _ctl_fops = {
.open = nonseekable_open,
.unlocked_ioctl = nvm_ctl_ioctl,
.owner = THIS_MODULE,
.llseek = noop_llseek,
};
struct miscdevice _nvm_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "lightnvm",
.nodename = "lightnvm/control",
.fops = &_ctl_fops,
};
再调用宏builtin_misc_device(_nvm_misc)来对设备进行注册
让我们看看builtin_misc_device
#define builtin_misc_device(__misc_device) \ builtin_driver(__misc_device, misc_register)
builtin_driver也是宏,定义如下:
#define builtin_driver(__driver, __register, ...) \ static int __init __driver##_init(void) \ { \ return __register(&(__driver) , ##__VA_ARGS__); \ } \ device_initcall(__driver##_init);
即builtin_driver定义了_nvm_misc_init函数,并通过device_initcall调用该函数。
device_initcall涉及到linux设备驱动,详解见linux驱动 之 module_init解析 (上),其中有device_initcall的详细介绍;
device_initcall(_nvm_misc_init) ---> __define_initcall(_nvm_misc_init, 6)
最终会将_nvm_misc_init放在.initcall6.init段中,注册的函数在kernel启动的时候会按照优先级被顺序执行;
即kernel启动时,会执行_nvm_misc_init函数,该函数会调用misc_register函数。
(builtin_misc_device适用于无需exit模块的设备的注册)
4.misc_register注册miscdevice
内核中misc_register函数
/*
*misc_register - 注册一个杂项设备
*@misc:设备结构
*/
int misc_register(struct miscdevice *misc)
{
dev_t dev;
int err = 0;
bool is_dynamic = (misc->minor == MISC_DYNAMIC_MINOR);
INIT_LIST_HEAD(&misc->list);
mutex_lock(&misc_mtx);
if (is_dynamic) {
int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
if (i >= DYNAMIC_MINORS) {
err = -EBUSY;
goto out;
}
misc->minor = DYNAMIC_MINORS - i - 1;
set_bit(i, misc_minors);
} else {
struct miscdevice *c;
list_for_each_entry(c, &misc_list, list) {
if (c->minor == misc->minor) {
err = -EBUSY;
goto out;
}
}
}
dev = MKDEV(MISC_MAJOR, misc->minor); /*计算出设备号*/
misc->this_device =
device_create_with_groups(misc_class, misc->parent, dev,
misc, misc->groups, "%s", misc->name);
if (IS_ERR(misc->this_device)) {
if (is_dynamic) {
int i = DYNAMIC_MINORS - misc->minor - 1;
if (i < DYNAMIC_MINORS && i >= 0)
clear_bit(i, misc_minors);
misc->minor = MISC_DYNAMIC_MINOR;
}
err = PTR_ERR(misc->this_device);
goto out;
}
/*
* Add it to the front, so that later devices can "override"
* earlier defaults
*/
list_add(&misc->list, &misc_list); /*将这个miscdevice添加到misc_list链表中*/
out:
mutex_unlock(&misc_mtx);
return err;
}
misc_register会在内核中注册一个 _nvm_misc设备。
次要编号设置为MISC_DYNAMIC_MINOR,则分配次编号并将其放置在结构的minor字段中,
调用device_create_with_groups ,创建一个设备并将其注册到sysfs,
传递的结构链接到内核中,在取消注册之前不得销毁。
所有的miscdevice设备形成了一个链表,对设备访问时内核根据次设备号查找对应的miscdevice设备,然后调用其file_operations结构中注册的文件操作接口进行操作。
这样就能通过file_operations中的注册的操作接口对设备进行操作。