1.关键数据结构
include/linux/cdev.h
struct cdev {
//内核驱动基本对象
struct kobject kobj;
//相关内核模块
struct module *owner;
//设备驱动接口
const struct file_operations *ops;
//链表节点
struct list_head list;
//设备号
dev_t dev;
//次设备号的数量
unsigned int count;
} __randomize_layout;
linux/base/map.c
struct kobj_map {
struct probe {
//指向下一个链表节点
struct probe *next;
//设备号
dev_t dev;
//次设备号的数量
unsigned long range;
struct module *owner;
kobj_probe_t *get;
int (*lock)(dev_t, void *);
//空指针,内核常用技巧
void *data;
} *probes[255];
struct mutex *lock;
};
2.cdev_init函数分析
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
{
memset(cdev, 0, sizeof *cdev);
INIT_LIST_HEAD(&cdev->list);
kobject_init(&cdev->kobj, &ktype_cdev_default);
cdev->ops = fops;
}
保存file_operation到cdev的ops中
3.cdev_add函数分析
include/linux/cdev.h
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
{
int error;
//保存设备号
p->dev = dev;
//保存次设备号数量
p->count = count;
if (WARN_ON(dev == WHITEOUT_DEV))
return -EBUSY;
//cdev_map为一个全局数组
error = kobj_map(cdev_map, dev, count, NULL,
exact_match, exact_lock, p);
if (error)
return error;
kobject_get(p->kobj.parent);
return 0;
}
3.1 kobj_map函数分析
linux/base/map.c
int kobj_map(struct kobj_map *domain, dev_t dev, unsigned long range,
struct module *module, kobj_probe_t *probe,
int (*lock)(dev_t, void *), void *data)
{
//判断次设备号数量是否溢出,最大1M
unsigned n = MAJOR(dev + range - 1) - MAJOR(dev) + 1;
//获取到主设备号,设备号的高12位
unsigned index = MAJOR(dev);
unsigned i;
struct probe *p;
if (n > 255)
n = 255;
//申请n个probe结构体的大小
p = kmalloc_array(n, sizeof(struct probe), GFP_KERNEL);
if (p == NULL)
return -ENOMEM;
for (i = 0; i < n; i++, p++) {
p->owner = module;
p->get = probe;
p->lock = lock;
p->dev = dev;//保存设备号
p->range = range;//保存次设备号数量
p->data = data;//保存cdev结构体
}
mutex_lock(domain->lock);
for (i = 0, p -= n; i < n; i++, p++, index++) {
struct probe **s = &domain->probes[index % 255];
while (*s && (*s)->range < range)
s = &(*s)->next;
p->next = *s;
*s = p;
}
mutex_unlock(domain->lock);
return 0;
}
4.结论
根据哈希函数将cdev保存到probes哈希表中,方便内核查找file_operation使用