linux设备模型bus,device,driver,Kobject和Kset及设备驱动模型--Apple的学习笔记

前言:

今天我主要想学习linux驱动开发中的对象关联关系,网上搜索了下linux设备模型其实是从Kobject和Kset开始的。之前我是直接从bus/device/drive开始学习的,所以就补充下这块内容吧

一,Kobject和Kset代码分析

Kobject和Kset若站在sysfs角度来看就是创建对应的文件夹。从而建立起设备驱动框架。

我从driver_init开始看的。做下笔记

b317c58c25c0

image.png

devices_init函数主要作用就是建立/sys/devices、/sys/dev这两个顶级容器节点和/sys/dev/block、/sys/dev/char这两个二级节点,如上图灰色虚线框。

int __init devices_init(void)

{

devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);

if (!devices_kset)

return -ENOMEM;

dev_kobj = kobject_create_and_add("dev", NULL);

if (!dev_kobj)

goto dev_kobj_err;

sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);

if (!sysfs_dev_block_kobj)

goto block_kobj_err;

sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);

if (!sysfs_dev_char_kobj)

goto char_kobj_err;

return 0;

char_kobj_err:

kobject_put(sysfs_dev_block_kobj);

block_kobj_err:

kobject_put(dev_kobj);

dev_kobj_err:

kset_unregister(devices_kset);

return -ENOMEM;

}

主要关系

b317c58c25c0

image.png

二,bus_register代码分析

platform_bus_init->bus_register传输参数为

struct bus_type platform_bus_type = {

.name = "platform",

.dev_groups = platform_dev_groups,

.match = platform_match,

.uevent = platform_uevent,

.dma_configure = platform_dma_configure,

.pm = &platform_dev_pm_ops,

};

主要对私有成员subsys_private进行初始化,然后创建platform根目标及它的driver和device文件夹及bus属性文件。如下灰色虚线框

b317c58c25c0

image.png

对应c代码

int bus_register(struct bus_type *bus)

{

int retval;

struct subsys_private *priv;

struct lock_class_key *key = &bus->lock_key;

priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);

if (!priv)

return -ENOMEM;

priv->bus = bus;

bus->p = priv;

BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);

retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);

if (retval)

goto out;

priv->subsys.kobj.kset = bus_kset;

priv->subsys.kobj.ktype = &bus_ktype;

priv->drivers_autoprobe = 1;

retval = kset_register(&priv->subsys);

if (retval)

goto out;

retval = bus_create_file(bus, &bus_attr_uevent);

if (retval)

goto bus_uevent_fail;

priv->devices_kset = kset_create_and_add("devices", NULL,

&priv->subsys.kobj);

if (!priv->devices_kset) {

retval = -ENOMEM;

goto bus_devices_fail;

}

priv->drivers_kset = kset_create_and_add("drivers", NULL,

&priv->subsys.kobj);

if (!priv->drivers_kset) {

retval = -ENOMEM;

goto bus_drivers_fail;

}

INIT_LIST_HEAD(&priv->interfaces);

__mutex_init(&priv->mutex, "subsys mutex", key);

klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);

klist_init(&priv->klist_drivers, NULL, NULL);

retval = add_probe_files(bus);

if (retval)

goto bus_probe_files_fail;

retval = bus_add_groups(bus, bus->bus_groups);

if (retval)

goto bus_groups_fail;

pr_debug("bus: '%s': registered\n", bus->name);

return 0;

bus_groups_fail:

remove_probe_files(bus);

bus_probe_files_fail:

kset_unregister(bus->p->drivers_kset);

bus_drivers_fail:

kset_unregister(bus->p->devices_kset);

bus_devices_fail:

bus_remove_file(bus, &bus_attr_uevent);

bus_uevent_fail:

kset_unregister(&bus->p->subsys);

out:

kfree(bus->p);

bus->p = NULL;

return retval;

}

EXPORT_SYMBOL_GPL(bus_register);

三,小插曲container_of和list_for_each_entry

b317c58c25c0

image.png

TYPE *将整型常量0强制转换为TYPE型的指针,且这个指针指向的地址为0,也就是将地址0开始的一块存储空间映射为TYPE型的对象,接下来再对结构体中MEMBER成员进行取址,而整个TYPE结构体的首地址是0,这里获得的地址就是MEMBER成员在TYPE中的相对偏移量。再将这个偏移量强制转换成size_t型数据(无符号整型)

list_for_each_entry实际上是一个 for 循环,利用传入的 pos 作为循环变量,从表头 head 开始,逐项向后(next 方向)移动 pos,直至又回head。参考网址# list_for_each_entry解析

linux中双向链表及链表头的用法:

建立一个双向链表通常有一个独立的用于管理链表的链表头,链表头一般是不含有实体数据的,必须用INIT_LIST_HEAD()进行初始化,表头建立以后,就可以将带有数据结构的实体链表成员加入到链。

四,device_register函数

platform_bus_init->device_register归纳为初始化device的链表和锁及创建文件夹及属性文件,灰色虚线框中主要是设置了自动匹配的话,则会尝试搜索对应驱动进行绑定。

b317c58c25c0

image.png

注:此时经常看到klist_add_tail

理解上的技巧,就是将node添加到list中,所以看到xxx->node和xxx->list就是是哪个加入到哪里了。

klist_add_tail(&dev->p->knode_parent,&parent->p->klist_children);

klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);

driver_register和device_register类似

module_init中我们添加驱动一般都会调用platform_driver_register,此函数会调用driver_register。都是通过bus总线这个媒人来互相配对,哈哈~

driver_match_device先调用匹配函数,然后在会调用of_driver_match_device这个函数最终调用到__of_match_node()函数,在这个函数里,通过把device_driver的of_match_table(of_device_id结构体的数组)和device里的of_node(device_node结构体)进行匹配,匹配方式是分别比较两者的name、type、和compatible字符串,三者要同时相同(一般name、和type为空,只比较compatible字符串,比较compatible时,是比较整个字符串,不管字符串里的逗号的,直接compare整个字符串)。

五,总结

通过从初始化函数中看了Kobject和Kset,同时复习了下bus/device/driver的匹配过程。终于从点到面,更加理解sys文件夹下的目录结构已经了解了sysfs的文件创建过程,device和driver搜索是通过bus中的list挨个查询,感觉理解又变的生动了,大脑中已经建立一个右bus作为树干device和driver作为树枝的关系图了。

六,参考网址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值